openarena_0.8.8.orig/0000755000175000017500000000000011720471601013221 5ustar smcvsmcvopenarena_0.8.8.orig/README-0.8.80000644000175000017500000000047011720471601014553 0ustar smcvsmcvThis is the GPLv2+ source for the game code of OpenArena 0.8.8 Compiling should be as simple as typing "make" or double clicking the windows scripts. This is taken from the oa-0.8.8 branch of http://code.google.com/p/oax/ at revision 295. More information can likely be found at http://openarena.ws/board/ openarena_0.8.8.orig/code/0000755000175000017500000000000011720470201014126 5ustar smcvsmcvopenarena_0.8.8.orig/code/ui/0000755000175000017500000000000011720470203014545 5ustar smcvsmcvopenarena_0.8.8.orig/code/ui/ui_local.h0000644000175000017500000007252311656310265016527 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifndef __UI_LOCAL_H__ #define __UI_LOCAL_H__ #include "../qcommon/q_shared.h" #include "../renderer/tr_types.h" #include "ui_public.h" #include "../client/keycodes.h" #include "../game/bg_public.h" #include "ui_shared.h" // global display context extern vmCvar_t ui_ffa_fraglimit; extern vmCvar_t ui_ffa_timelimit; extern vmCvar_t ui_tourney_fraglimit; extern vmCvar_t ui_tourney_timelimit; extern vmCvar_t ui_team_fraglimit; extern vmCvar_t ui_team_timelimit; extern vmCvar_t ui_team_friendly; extern vmCvar_t ui_ctf_capturelimit; extern vmCvar_t ui_ctf_timelimit; extern vmCvar_t ui_ctf_friendly; extern vmCvar_t ui_arenasFile; extern vmCvar_t ui_botsFile; extern vmCvar_t ui_spScores1; extern vmCvar_t ui_spScores2; extern vmCvar_t ui_spScores3; extern vmCvar_t ui_spScores4; extern vmCvar_t ui_spScores5; extern vmCvar_t ui_spAwards; extern vmCvar_t ui_spVideos; extern vmCvar_t ui_spSkill; extern vmCvar_t ui_spSelection; extern vmCvar_t ui_browserMaster; extern vmCvar_t ui_browserGameType; extern vmCvar_t ui_browserSortKey; extern vmCvar_t ui_browserShowFull; extern vmCvar_t ui_browserShowEmpty; extern vmCvar_t ui_brassTime; extern vmCvar_t ui_drawCrosshair; extern vmCvar_t ui_drawCrosshairNames; extern vmCvar_t ui_marks; extern vmCvar_t ui_server1; extern vmCvar_t ui_server2; extern vmCvar_t ui_server3; extern vmCvar_t ui_server4; extern vmCvar_t ui_server5; extern vmCvar_t ui_server6; extern vmCvar_t ui_server7; extern vmCvar_t ui_server8; extern vmCvar_t ui_server9; extern vmCvar_t ui_server10; extern vmCvar_t ui_server11; extern vmCvar_t ui_server12; extern vmCvar_t ui_server13; extern vmCvar_t ui_server14; extern vmCvar_t ui_server15; extern vmCvar_t ui_server16; extern vmCvar_t ui_cdkey; extern vmCvar_t ui_cdkeychecked; extern vmCvar_t ui_captureLimit; extern vmCvar_t ui_fragLimit; extern vmCvar_t ui_gameType; extern vmCvar_t ui_netGameType; extern vmCvar_t ui_actualNetGameType; extern vmCvar_t ui_joinGameType; extern vmCvar_t ui_netSource; extern vmCvar_t ui_serverFilterType; extern vmCvar_t ui_dedicated; extern vmCvar_t ui_opponentName; extern vmCvar_t ui_menuFiles; extern vmCvar_t ui_currentTier; extern vmCvar_t ui_currentMap; extern vmCvar_t ui_currentNetMap; extern vmCvar_t ui_mapIndex; extern vmCvar_t ui_currentOpponent; extern vmCvar_t ui_selectedPlayer; extern vmCvar_t ui_selectedPlayerName; extern vmCvar_t ui_lastServerRefresh_0; extern vmCvar_t ui_lastServerRefresh_1; extern vmCvar_t ui_lastServerRefresh_2; extern vmCvar_t ui_lastServerRefresh_3; extern vmCvar_t ui_singlePlayerActive; extern vmCvar_t ui_scoreAccuracy; extern vmCvar_t ui_scoreImpressives; extern vmCvar_t ui_scoreExcellents; extern vmCvar_t ui_scoreDefends; extern vmCvar_t ui_scoreAssists; extern vmCvar_t ui_scoreGauntlets; extern vmCvar_t ui_scoreScore; extern vmCvar_t ui_scorePerfect; extern vmCvar_t ui_scoreTeam; extern vmCvar_t ui_scoreBase; extern vmCvar_t ui_scoreTimeBonus; extern vmCvar_t ui_scoreSkillBonus; extern vmCvar_t ui_scoreShutoutBonus; extern vmCvar_t ui_scoreTime; extern vmCvar_t ui_smallFont; extern vmCvar_t ui_bigFont; extern vmCvar_t ui_serverStatusTimeOut; extern vmCvar_t ui_humansonly; // // ui_qmenu.c // #define RCOLUMN_OFFSET ( BIGCHAR_WIDTH ) #define LCOLUMN_OFFSET (-BIGCHAR_WIDTH ) #define SLIDER_RANGE 10 #define MAX_EDIT_LINE 256 #define MAX_MENUDEPTH 8 #define MAX_MENUITEMS 96 #define MTYPE_NULL 0 #define MTYPE_SLIDER 1 #define MTYPE_ACTION 2 #define MTYPE_SPINCONTROL 3 #define MTYPE_FIELD 4 #define MTYPE_RADIOBUTTON 5 #define MTYPE_BITMAP 6 #define MTYPE_TEXT 7 #define MTYPE_SCROLLLIST 8 #define MTYPE_PTEXT 9 #define MTYPE_BTEXT 10 #define QMF_BLINK 0x00000001 #define QMF_SMALLFONT 0x00000002 #define QMF_LEFT_JUSTIFY 0x00000004 #define QMF_CENTER_JUSTIFY 0x00000008 #define QMF_RIGHT_JUSTIFY 0x00000010 #define QMF_NUMBERSONLY 0x00000020 // edit field is only numbers #define QMF_HIGHLIGHT 0x00000040 #define QMF_HIGHLIGHT_IF_FOCUS 0x00000080 // steady focus #define QMF_PULSEIFFOCUS 0x00000100 // pulse if focus #define QMF_HASMOUSEFOCUS 0x00000200 #define QMF_NOONOFFTEXT 0x00000400 #define QMF_MOUSEONLY 0x00000800 // only mouse input allowed #define QMF_HIDDEN 0x00001000 // skips drawing #define QMF_GRAYED 0x00002000 // grays and disables #define QMF_INACTIVE 0x00004000 // disables any input #define QMF_NODEFAULTINIT 0x00008000 // skip default initialization #define QMF_OWNERDRAW 0x00010000 #define QMF_PULSE 0x00020000 #define QMF_LOWERCASE 0x00040000 // edit field is all lower case #define QMF_UPPERCASE 0x00080000 // edit field is all upper case #define QMF_SILENT 0x00100000 // callback notifications #define QM_GOTFOCUS 1 #define QM_LOSTFOCUS 2 #define QM_ACTIVATED 3 typedef struct _tag_menuframework { int cursor; int cursor_prev; int nitems; void *items[MAX_MENUITEMS]; void (*draw) (void); sfxHandle_t (*key) (int key); qboolean wrapAround; qboolean fullscreen; qboolean showlogo; } menuframework_s; typedef struct { int type; const char *name; int id; int x, y; int left; int top; int right; int bottom; menuframework_s *parent; int menuPosition; unsigned flags; void (*callback)( void *self, int event ); void (*statusbar)( void *self ); void (*ownerdraw)( void *self ); } menucommon_s; typedef struct { int cursor; int scroll; int widthInChars; char buffer[MAX_EDIT_LINE]; int maxchars; } mfield_t; typedef struct { menucommon_s generic; mfield_t field; } menufield_s; typedef struct { menucommon_s generic; float minvalue; float maxvalue; float curvalue; float range; } menuslider_s; typedef struct { menucommon_s generic; int oldvalue; int curvalue; int numitems; int top; const char **itemnames; int width; int height; int columns; int seperation; } menulist_s; typedef struct { menucommon_s generic; } menuaction_s; typedef struct { menucommon_s generic; int curvalue; } menuradiobutton_s; typedef struct { menucommon_s generic; char* focuspic; char* errorpic; qhandle_t shader; qhandle_t focusshader; int width; int height; float* focuscolor; } menubitmap_s; typedef struct { menucommon_s generic; char* string; int style; float* color; } menutext_s; extern void Menu_Cache( void ); extern void Menu_Focus( menucommon_s *m ); extern void Menu_AddItem( menuframework_s *menu, void *item ); extern void Menu_AdjustCursor( menuframework_s *menu, int dir ); extern void Menu_Draw( menuframework_s *menu ); extern void *Menu_ItemAtCursor( menuframework_s *m ); extern sfxHandle_t Menu_ActivateItem( menuframework_s *s, menucommon_s* item ); extern void Menu_SetCursor( menuframework_s *s, int cursor ); extern void Menu_SetCursorToItem( menuframework_s *m, void* ptr ); extern sfxHandle_t Menu_DefaultKey( menuframework_s *s, int key ); extern void Bitmap_Init( menubitmap_s *b ); extern void Bitmap_Draw( menubitmap_s *b ); extern void ScrollList_Draw( menulist_s *l ); extern sfxHandle_t ScrollList_Key( menulist_s *l, int key ); extern sfxHandle_t menu_in_sound; extern sfxHandle_t menu_move_sound; extern sfxHandle_t menu_out_sound; extern sfxHandle_t menu_buzz_sound; extern sfxHandle_t menu_null_sound; extern sfxHandle_t weaponChangeSound; extern vec4_t menu_text_color; extern vec4_t menu_grayed_color; extern vec4_t menu_dark_color; extern vec4_t menu_highlight_color; extern vec4_t menu_red_color; extern vec4_t menu_black_color; extern vec4_t menu_dim_color; extern vec4_t color_black; extern vec4_t color_white; extern vec4_t color_yellow; extern vec4_t color_blue; extern vec4_t color_orange; extern vec4_t color_red; extern vec4_t color_dim; extern vec4_t name_color; extern vec4_t list_color; extern vec4_t listbar_color; extern vec4_t text_color_disabled; extern vec4_t text_color_normal; extern vec4_t text_color_highlight; extern char *ui_medalNames[]; extern char *ui_medalPicNames[]; extern char *ui_medalSounds[]; // // ui_mfield.c // extern void MField_Clear( mfield_t *edit ); extern void MField_KeyDownEvent( mfield_t *edit, int key ); extern void MField_CharEvent( mfield_t *edit, int ch ); extern void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ); extern void MenuField_Init( menufield_s* m ); extern void MenuField_Draw( menufield_s *f ); extern sfxHandle_t MenuField_Key( menufield_s* m, int* key ); // // ui_main.c // void UI_Report( void ); void UI_Load( void ); void UI_LoadMenus(const char *menuFile, qboolean reset); void _UI_SetActiveMenu( uiMenuCommand_t menu ); int UI_AdjustTimeByGame(int time); void UI_ShowPostGame(qboolean newHigh); void UI_ClearScores( void ); void UI_LoadArenas(void); // // ui_menu.c // extern void MainMenu_Cache( void ); extern void UI_MainMenu(void); extern void UI_RegisterCvars( void ); extern void UI_UpdateCvars( void ); // // ui_credits.c // extern void UI_CreditMenu( void ); // // ui_ingame.c // extern void InGame_Cache( void ); extern void UI_InGameMenu(void); // // ui_confirm.c // extern void ConfirmMenu_Cache( void ); extern void UI_ConfirmMenu( const char *question, void (*draw)( void ), void (*action)( qboolean result ) ); // // ui_setup.c // extern void UI_SetupMenu_Cache( void ); extern void UI_SetupMenu(void); // // ui_team.c // extern void UI_TeamMainMenu( void ); extern void TeamMain_Cache( void ); // // ui_connect.c // extern void UI_DrawConnectScreen( qboolean overlay ); // // ui_controls2.c // extern void UI_ControlsMenu( void ); extern void Controls_Cache( void ); // // ui_demo2.c // extern void UI_DemosMenu( void ); extern void Demos_Cache( void ); // // ui_cinematics.c // extern void UI_CinematicsMenu( void ); extern void UI_CinematicsMenu_f( void ); extern void UI_CinematicsMenu_Cache( void ); // // ui_mods.c // extern void UI_ModsMenu( void ); extern void UI_ModsMenu_Cache( void ); // // ui_cdkey.c // extern void UI_CDKeyMenu( void ); extern void UI_CDKeyMenu_Cache( void ); extern void UI_CDKeyMenu_f( void ); // // ui_playermodel.c // extern void UI_PlayerModelMenu( void ); extern void PlayerModel_Cache( void ); // // ui_playersettings.c // extern void UI_PlayerSettingsMenu( void ); extern void PlayerSettings_Cache( void ); // // ui_preferences.c // extern void UI_PreferencesMenu( void ); extern void Preferences_Cache( void ); // // ui_specifyleague.c // extern void UI_SpecifyLeagueMenu( void ); extern void SpecifyLeague_Cache( void ); // // ui_specifyserver.c // extern void UI_SpecifyServerMenu( void ); extern void SpecifyServer_Cache( void ); // // ui_servers2.c // #define MAX_FAVORITESERVERS 16 extern void UI_ArenaServersMenu( void ); extern void ArenaServers_Cache( void ); // // ui_startserver.c // extern void UI_StartServerMenu( qboolean multiplayer ); extern void StartServer_Cache( void ); extern void ServerOptions_Cache( void ); extern void UI_BotSelectMenu( char *bot ); extern void UI_BotSelectMenu_Cache( void ); // // ui_serverinfo.c // extern void UI_ServerInfoMenu( void ); extern void ServerInfo_Cache( void ); // // ui_video.c // extern void UI_GraphicsOptionsMenu( void ); extern void GraphicsOptions_Cache( void ); extern void DriverInfo_Cache( void ); // // ui_players.c // //FIXME ripped from cg_local.h typedef struct { int oldFrame; int oldFrameTime; // time when ->oldFrame was exactly on int frame; int frameTime; // time when ->frame will be exactly on float backlerp; float yawAngle; qboolean yawing; float pitchAngle; qboolean pitching; int animationNumber; // may include ANIM_TOGGLEBIT animation_t *animation; int animationTime; // time when the first frame of the animation will be exact } lerpFrame_t; typedef struct { // model info qhandle_t legsModel; qhandle_t legsSkin; lerpFrame_t legs; qhandle_t torsoModel; qhandle_t torsoSkin; lerpFrame_t torso; qhandle_t headModel; qhandle_t headSkin; animation_t animations[MAX_TOTALANIMATIONS]; qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; vec3_t flashDlightColor; int muzzleFlashTime; // currently in use drawing parms vec3_t viewAngles; vec3_t moveAngles; weapon_t currentWeapon; int legsAnim; int torsoAnim; // animation vars weapon_t weapon; weapon_t lastWeapon; weapon_t pendingWeapon; int weaponTimer; int pendingLegsAnim; int torsoAnimationTimer; int pendingTorsoAnim; int legsAnimationTimer; qboolean chat; qboolean newModel; qboolean barrelSpinning; float barrelAngle; int barrelTime; int realWeapon; } playerInfo_t; void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ); void UI_DrawPlayerII( float x, float y, float w, float h, playerInfo_t *pi, int time ); void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model, const char *headmodel, char *teamName ); void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNum, qboolean chat ); qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName , const char *headName, const char *teamName); // // ui_atoms.c // // this is only used in the old ui, the new ui has it's own version typedef struct { int frametime; int realtime; int cursorx; int cursory; glconfig_t glconfig; qboolean debug; qhandle_t whiteShader; qhandle_t menuBackShader; qhandle_t menuBackShader2; qhandle_t menuBackNoLogoShader; qhandle_t charset; qhandle_t charsetProp; qhandle_t charsetPropGlow; qhandle_t charsetPropB; qhandle_t cursor; qhandle_t rb_on; qhandle_t rb_off; float scale; float bias; qboolean demoversion; qboolean firstdraw; } uiStatic_t; // new ui stuff #define UI_NUMFX 7 #define MAX_HEADS 64 #define MAX_ALIASES 64 #define MAX_HEADNAME 32 #define MAX_TEAMS 64 #define MAX_GAMETYPES 16 #define MAX_MAPS 128 #define MAX_SPMAPS 16 #define PLAYERS_PER_TEAM 5 #define MAX_PINGREQUESTS 32 #define MAX_ADDRESSLENGTH 64 #define MAX_HOSTNAMELENGTH 22 #define MAX_MAPNAMELENGTH 16 #define MAX_STATUSLENGTH 64 #define MAX_LISTBOXWIDTH 59 #define UI_FONT_THRESHOLD 0.1 #define MAX_DISPLAY_SERVERS 2048 #define MAX_SERVERSTATUS_LINES 128 #define MAX_SERVERSTATUS_TEXT 1024 #define MAX_FOUNDPLAYER_SERVERS 16 #define TEAM_MEMBERS 5 #define GAMES_ALL 0 #define GAMES_FFA 1 #define GAMES_TEAMPLAY 2 #define GAMES_TOURNEY 3 #define GAMES_CTF 4 #define MAPS_PER_TIER 3 #define MAX_TIERS 16 #define MAX_MODS 64 #define MAX_DEMOS 256 #define MAX_MOVIES 256 //#define MAX_PLAYERMODELS 256 #define MAX_PLAYERMODELS 1024 typedef struct { const char *name; const char *imageName; qhandle_t headImage; const char *base; qboolean active; int reference; } characterInfo; typedef struct { const char *name; const char *ai; const char *action; } aliasInfo; typedef struct { const char *teamName; const char *imageName; const char *teamMembers[TEAM_MEMBERS]; qhandle_t teamIcon; qhandle_t teamIcon_Metal; qhandle_t teamIcon_Name; int cinematic; } teamInfo; typedef struct { const char *gameType; int gtEnum; } gameTypeInfo; typedef struct { const char *mapName; const char *mapLoadName; const char *imageName; const char *opponentName; int teamMembers; int typeBits; int cinematic; int timeToBeat[MAX_GAMETYPES]; qhandle_t levelShot; qboolean active; } mapInfo; typedef struct { const char *tierName; const char *maps[MAPS_PER_TIER]; int gameTypes[MAPS_PER_TIER]; qhandle_t mapHandles[MAPS_PER_TIER]; } tierInfo; typedef struct serverFilter_s { const char *description; const char *basedir; } serverFilter_t; typedef struct { char adrstr[MAX_ADDRESSLENGTH]; int start; } pinglist_t; typedef struct serverStatus_s { pinglist_t pingList[MAX_PINGREQUESTS]; int numqueriedservers; int currentping; int nextpingtime; int maxservers; int refreshtime; int numServers; int sortKey; int sortDir; int lastCount; qboolean refreshActive; int currentServer; int displayServers[MAX_DISPLAY_SERVERS]; int numDisplayServers; int numPlayersOnServers; int nextDisplayRefresh; int nextSortTime; qhandle_t currentServerPreview; int currentServerCinematic; int motdLen; int motdWidth; int motdPaintX; int motdPaintX2; int motdOffset; int motdTime; char motd[MAX_STRING_CHARS]; } serverStatus_t; typedef struct { char adrstr[MAX_ADDRESSLENGTH]; char name[MAX_ADDRESSLENGTH]; int startTime; int serverNum; qboolean valid; } pendingServer_t; typedef struct { int num; pendingServer_t server[MAX_SERVERSTATUSREQUESTS]; } pendingServerStatus_t; typedef struct { char address[MAX_ADDRESSLENGTH]; char *lines[MAX_SERVERSTATUS_LINES][4]; char text[MAX_SERVERSTATUS_TEXT]; char pings[MAX_CLIENTS * 3]; int numLines; } serverStatusInfo_t; typedef struct { const char *modName; const char *modDescr; } modInfo_t; typedef struct { displayContextDef_t uiDC; int newHighScoreTime; int newBestTime; int showPostGameTime; qboolean newHighScore; qboolean demoAvailable; qboolean soundHighScore; int characterCount; int botIndex; characterInfo characterList[MAX_HEADS]; int aliasCount; aliasInfo aliasList[MAX_ALIASES]; int teamCount; teamInfo teamList[MAX_TEAMS]; int numGameTypes; gameTypeInfo gameTypes[MAX_GAMETYPES]; int numJoinGameTypes; gameTypeInfo joinGameTypes[MAX_GAMETYPES]; int redBlue; int playerCount; int myTeamCount; int teamIndex; int playerRefresh; int playerIndex; int playerNumber; qboolean teamLeader; char playerNames[MAX_CLIENTS][MAX_NAME_LENGTH]; char teamNames[MAX_CLIENTS][MAX_NAME_LENGTH]; int teamClientNums[MAX_CLIENTS]; int mapCount; mapInfo mapList[MAX_MAPS]; int tierCount; tierInfo tierList[MAX_TIERS]; int skillIndex; modInfo_t modList[MAX_MODS]; int modCount; int modIndex; const char *demoList[MAX_DEMOS]; int demoCount; int demoIndex; const char *movieList[MAX_MOVIES]; int movieCount; int movieIndex; int previewMovie; serverStatus_t serverStatus; // for the showing the status of a server char serverStatusAddress[MAX_ADDRESSLENGTH]; serverStatusInfo_t serverStatusInfo; int nextServerStatusRefresh; // to retrieve the status of server to find a player pendingServerStatus_t pendingServerStatus; char findPlayerName[MAX_STRING_CHARS]; char foundPlayerServerAddresses[MAX_FOUNDPLAYER_SERVERS][MAX_ADDRESSLENGTH]; char foundPlayerServerNames[MAX_FOUNDPLAYER_SERVERS][MAX_ADDRESSLENGTH]; int currentFoundPlayerServer; int numFoundPlayerServers; int nextFindPlayerRefresh; int currentCrosshair; int startPostGameTime; sfxHandle_t newHighScoreSound; int q3HeadCount; char q3HeadNames[MAX_PLAYERMODELS][64]; qhandle_t q3HeadIcons[MAX_PLAYERMODELS]; int q3SelectedHead; int effectsColor; qboolean inGameLoad; } uiInfo_t; extern uiInfo_t uiInfo; extern void UI_Init( void ); extern void UI_Shutdown( void ); extern void UI_KeyEvent( int key ); extern void UI_MouseEvent( int dx, int dy ); extern void UI_Refresh( int realtime ); extern qboolean UI_ConsoleCommand( int realTime ); extern float UI_ClampCvar( float min, float max, float value ); extern void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ); extern void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ); extern void UI_FillRect( float x, float y, float width, float height, const float *color ); extern void UI_DrawRect( float x, float y, float width, float height, const float *color ); extern void UI_DrawTopBottom(float x, float y, float w, float h); extern void UI_DrawSides(float x, float y, float w, float h); extern void UI_UpdateScreen( void ); extern void UI_SetColor( const float *rgba ); extern void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t); extern void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ); extern float UI_ProportionalSizeScale( int style ); extern void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); extern int UI_ProportionalStringWidth( const char* str ); extern void UI_DrawString( int x, int y, const char* str, int style, vec4_t color ); extern void UI_DrawChar( int x, int y, int ch, int style, vec4_t color ); extern qboolean UI_CursorInRect (int x, int y, int width, int height); extern void UI_AdjustFrom640( float *x, float *y, float *w, float *h ); extern void UI_DrawTextBox (int x, int y, int width, int lines); extern qboolean UI_IsFullscreen( void ); extern void UI_SetActiveMenu( uiMenuCommand_t menu ); extern void UI_PushMenu ( menuframework_s *menu ); extern void UI_PopMenu (void); extern void UI_ForceMenuOff (void); extern char *UI_Argv( int arg ); extern char *UI_Cvar_VariableString( const char *var_name ); extern void UI_Refresh( int time ); extern void UI_KeyEvent( int key ); extern void UI_StartDemoLoop( void ); extern qboolean m_entersound; void UI_LoadBestScores(const char *map, int game); extern uiStatic_t uis; // // ui_spLevel.c // void UI_SPLevelMenu_Cache( void ); void UI_SPLevelMenu( void ); void UI_SPLevelMenu_f( void ); void UI_SPLevelMenu_ReInit( void ); // // ui_spArena.c // void UI_SPArena_Start( const char *arenaInfo ); // // ui_spPostgame.c // void UI_SPPostgameMenu_Cache( void ); void UI_SPPostgameMenu_f( void ); // // ui_spSkill.c // void UI_SPSkillMenu( const char *arenaInfo ); void UI_SPSkillMenu_Cache( void ); // // ui_syscalls.c // void trap_Print( const char *string ); void trap_Error( const char *string ); int trap_Milliseconds( void ); void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); void trap_Cvar_Update( vmCvar_t *vmCvar ); void trap_Cvar_Set( const char *var_name, const char *value ); float trap_Cvar_VariableValue( const char *var_name ); void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void trap_Cvar_SetValue( const char *var_name, float value ); void trap_Cvar_Reset( const char *name ); void trap_Cvar_Create( const char *var_name, const char *var_value, int flags ); void trap_Cvar_InfoStringBuffer( int bit, char *buffer, int bufsize ); int trap_Argc( void ); void trap_Argv( int n, char *buffer, int bufferLength ); void trap_Cmd_ExecuteText( int exec_when, const char *text ); // don't use EXEC_NOW! int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); void trap_FS_Read( void *buffer, int len, fileHandle_t f ); void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); void trap_FS_FCloseFile( fileHandle_t f ); int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t qhandle_t trap_R_RegisterModel( const char *name ); qhandle_t trap_R_RegisterSkin( const char *name ); qhandle_t trap_R_RegisterShaderNoMip( const char *name ); void trap_R_ClearScene( void ); void trap_R_AddRefEntityToScene( const refEntity_t *re ); void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ); void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); void trap_R_RenderScene( const refdef_t *fd ); void trap_R_SetColor( const float *rgba ); void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); void trap_UpdateScreen( void ); int trap_CM_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ); void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ); void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ); void trap_Key_SetBinding( int keynum, const char *binding ); qboolean trap_Key_IsDown( int keynum ); qboolean trap_Key_GetOverstrikeMode( void ); void trap_Key_SetOverstrikeMode( qboolean state ); void trap_Key_ClearStates( void ); int trap_Key_GetCatcher( void ); void trap_Key_SetCatcher( int catcher ); void trap_GetClipboardData( char *buf, int bufsize ); void trap_GetClientState( uiClientState_t *state ); void trap_GetGlconfig( glconfig_t *glconfig ); int trap_GetConfigString( int index, char* buff, int buffsize ); int trap_LAN_GetServerCount( int source ); void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen ); void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen ); int trap_LAN_GetServerPing( int source, int n ); int trap_LAN_GetPingQueueCount( void ); void trap_LAN_ClearPing( int n ); void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime ); void trap_LAN_GetPingInfo( int n, char *buf, int buflen ); void trap_LAN_LoadCachedServers( void ); void trap_LAN_SaveCachedServers( void ); void trap_LAN_MarkServerVisible(int source, int n, qboolean visible); int trap_LAN_ServerIsVisible( int source, int n); qboolean trap_LAN_UpdateVisiblePings( int source ); int trap_LAN_AddServer(int source, const char *name, const char *addr); void trap_LAN_RemoveServer(int source, const char *addr); void trap_LAN_ResetPings(int n); int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen ); int trap_LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ); int trap_MemoryRemaining( void ); void trap_GetCDKey( char *buf, int buflen ); void trap_SetCDKey( char *buf ); void trap_R_RegisterFont(const char *pFontname, int pointSize, fontInfo_t *font); void trap_S_StopBackgroundTrack( void ); void trap_S_StartBackgroundTrack( const char *intro, const char *loop); int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status trap_CIN_StopCinematic(int handle); e_status trap_CIN_RunCinematic (int handle); void trap_CIN_DrawCinematic (int handle); void trap_CIN_SetExtents (int handle, int x, int y, int w, int h); int trap_RealTime(qtime_t *qtime); void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); qboolean trap_VerifyCDKey( const char *key, const char *chksum); void trap_SetPbClStatus( int status ); // // ui_addbots.c // void UI_AddBots_Cache( void ); void UI_AddBotsMenu( void ); // // ui_removebots.c // void UI_RemoveBots_Cache( void ); void UI_RemoveBotsMenu( void ); // // ui_teamorders.c // extern void UI_TeamOrdersMenu( void ); extern void UI_TeamOrdersMenu_f( void ); extern void UI_TeamOrdersMenu_Cache( void ); // // ui_loadconfig.c // void UI_LoadConfig_Cache( void ); void UI_LoadConfigMenu( void ); // // ui_saveconfig.c // void UI_SaveConfigMenu_Cache( void ); void UI_SaveConfigMenu( void ); // // ui_display.c // void UI_DisplayOptionsMenu_Cache( void ); void UI_DisplayOptionsMenu( void ); // // ui_sound.c // void UI_SoundOptionsMenu_Cache( void ); void UI_SoundOptionsMenu( void ); // // ui_network.c // void UI_NetworkOptionsMenu_Cache( void ); void UI_NetworkOptionsMenu( void ); // // ui_gameinfo.c // typedef enum { AWARD_ACCURACY, AWARD_IMPRESSIVE, AWARD_EXCELLENT, AWARD_GAUNTLET, AWARD_FRAGS, AWARD_PERFECT } awardType_t; const char *UI_GetArenaInfoByNumber( int num ); const char *UI_GetArenaInfoByMap( const char *map ); const char *UI_GetSpecialArenaInfo( const char *tag ); int UI_GetNumArenas( void ); int UI_GetNumSPArenas( void ); int UI_GetNumSPTiers( void ); char *UI_GetBotInfoByNumber( int num ); char *UI_GetBotInfoByName( const char *name ); int UI_GetNumBots( void ); void UI_LoadBots( void ); char *UI_GetBotNameByNumber( int num ); void UI_GetBestScore( int level, int *score, int *skill ); void UI_SetBestScore( int level, int score ); int UI_TierCompleted( int levelWon ); qboolean UI_ShowTierVideo( int tier ); qboolean UI_CanShowTierVideo( int tier ); int UI_GetCurrentGame( void ); void UI_NewGame( void ); void UI_LogAwardData( int award, int data ); int UI_GetAwardLevel( int award ); void UI_SPUnlock_f( void ); void UI_SPUnlockMedals_f( void ); void UI_InitGameinfo( void ); // // ui_login.c // void Login_Cache( void ); void UI_LoginMenu( void ); // // ui_signup.c // void Signup_Cache( void ); void UI_SignupMenu( void ); // // ui_rankstatus.c // void RankStatus_Cache( void ); void UI_RankStatusMenu( void ); // new ui #define ASSET_BACKGROUND "uiBackground" // for tracking sp game info in Team Arena typedef struct postGameInfo_s { int score; int redScore; int blueScore; int perfects; int accuracy; int impressives; int excellents; int defends; int assists; int gauntlets; int captures; int time; int timeBonus; int shutoutBonus; int skillBonus; int baseScore; } postGameInfo_t; #endif openarena_0.8.8.orig/code/ui/ui_shared.c0000644000175000017500000044335211656310265016700 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // string allocation/managment #include "ui_shared.h" #define SCROLL_TIME_START 500 #define SCROLL_TIME_ADJUST 150 #define SCROLL_TIME_ADJUSTOFFSET 40 #define SCROLL_TIME_FLOOR 20 typedef struct scrollInfo_s { int nextScrollTime; int nextAdjustTime; int adjustValue; int scrollKey; float xStart; float yStart; itemDef_t *item; qboolean scrollDir; } scrollInfo_t; static scrollInfo_t scrollInfo; static void (*captureFunc) (void *p) = 0; static void *captureData = NULL; static itemDef_t *itemCapture = NULL; // item that has the mouse captured ( if any ) displayContextDef_t *DC = NULL; static qboolean g_waitingForKey = qfalse; static qboolean g_editingField = qfalse; static itemDef_t *g_bindItem = NULL; static itemDef_t *g_editItem = NULL; menuDef_t Menus[MAX_MENUS]; // defined menus int menuCount = 0; // how many menuDef_t *menuStack[MAX_OPEN_MENUS]; int openMenuCount = 0; static qboolean debugMode = qfalse; #define DOUBLE_CLICK_DELAY 300 static int lastListBoxClickTime = 0; void Item_RunScript(itemDef_t *item, const char *s); void Item_SetupKeywordHash(void); void Menu_SetupKeywordHash(void); int BindingIDFromName(const char *name); qboolean Item_Bind_HandleKey(itemDef_t *item, int key, qboolean down); itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu); itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu); static qboolean Menu_OverActiveItem(menuDef_t *menu, float x, float y); #ifdef CGAME #define MEM_POOL_SIZE 128 * 1024 #else #define MEM_POOL_SIZE 1024 * 1024 #endif static char memoryPool[MEM_POOL_SIZE]; static int allocPoint, outOfMemory; /* =============== UI_Alloc =============== */ void *UI_Alloc( int size ) { char *p; if ( allocPoint + size > MEM_POOL_SIZE ) { outOfMemory = qtrue; if (DC->Print) { DC->Print("UI_Alloc: Failure. Out of memory!\n"); } //DC->trap_Print(S_COLOR_YELLOW"WARNING: UI Out of Memory!\n"); return NULL; } p = &memoryPool[allocPoint]; allocPoint += ( size + 15 ) & ~15; return p; } /* =============== UI_InitMemory =============== */ void UI_InitMemory( void ) { allocPoint = 0; outOfMemory = qfalse; } qboolean UI_OutOfMemory( void ) { return outOfMemory; } #define HASH_TABLE_SIZE 2048 /* ================ return a hash value for the string ================ */ static unsigned hashForString(const char *str) { int i; unsigned hash; char letter; hash = 0; i = 0; while (str[i] != '\0') { letter = tolower(str[i]); hash+=(unsigned)(letter)*(i+119); i++; } hash &= (HASH_TABLE_SIZE-1); return hash; } typedef struct stringDef_s { struct stringDef_s *next; const char *str; } stringDef_t; static int strPoolIndex = 0; static char strPool[STRING_POOL_SIZE]; static int strHandleCount = 0; static stringDef_t *strHandle[HASH_TABLE_SIZE]; const char *String_Alloc(const char *p) { int len; unsigned hash; stringDef_t *str, *last; static const char *staticNULL = ""; if (p == NULL) { return NULL; } if (*p == 0) { return staticNULL; } hash = hashForString(p); str = strHandle[hash]; while (str) { if (strcmp(p, str->str) == 0) { return str->str; } str = str->next; } len = strlen(p); if (len + strPoolIndex + 1 < STRING_POOL_SIZE) { int ph = strPoolIndex; strcpy(&strPool[strPoolIndex], p); strPoolIndex += len + 1; str = strHandle[hash]; last = str; while (str && str->next) { last = str; str = str->next; } str = UI_Alloc(sizeof(stringDef_t)); str->next = NULL; str->str = &strPool[ph]; if (last) { last->next = str; } else { strHandle[hash] = str; } return &strPool[ph]; } return NULL; } void String_Report(void) { float f; Com_Printf("Memory/String Pool Info\n"); Com_Printf("----------------\n"); f = strPoolIndex; f /= STRING_POOL_SIZE; f *= 100; Com_Printf("String Pool is %.1f%% full, %i bytes out of %i used.\n", f, strPoolIndex, STRING_POOL_SIZE); f = allocPoint; f /= MEM_POOL_SIZE; f *= 100; Com_Printf("Memory Pool is %.1f%% full, %i bytes out of %i used.\n", f, allocPoint, MEM_POOL_SIZE); } /* ================= String_Init ================= */ void String_Init(void) { int i; for (i = 0; i < HASH_TABLE_SIZE; i++) { strHandle[i] = NULL; } strHandleCount = 0; strPoolIndex = 0; menuCount = 0; openMenuCount = 0; UI_InitMemory(); Item_SetupKeywordHash(); Menu_SetupKeywordHash(); if (DC && DC->getBindingBuf) { Controls_GetConfig(); } } /* ================= PC_SourceWarning ================= */ void PC_SourceWarning(int handle, char *format, ...) { int line; char filename[128]; va_list argptr; static char string[4096]; va_start (argptr, format); Q_vsnprintf (string, sizeof(string), format, argptr); va_end (argptr); filename[0] = '\0'; line = 0; trap_PC_SourceFileAndLine(handle, filename, &line); Com_Printf(S_COLOR_YELLOW "WARNING: %s, line %d: %s\n", filename, line, string); } /* ================= PC_SourceError ================= */ void PC_SourceError(int handle, char *format, ...) { int line; char filename[128]; va_list argptr; static char string[4096]; va_start (argptr, format); Q_vsnprintf (string, sizeof(string), format, argptr); va_end (argptr); filename[0] = '\0'; line = 0; trap_PC_SourceFileAndLine(handle, filename, &line); Com_Printf(S_COLOR_RED "ERROR: %s, line %d: %s\n", filename, line, string); } /* ================= LerpColor ================= */ void LerpColor(vec4_t a, vec4_t b, vec4_t c, float t) { int i; // lerp and clamp each component for (i=0; i<4; i++) { c[i] = a[i] + t*(b[i]-a[i]); if (c[i] < 0) c[i] = 0; else if (c[i] > 1.0) c[i] = 1.0; } } /* ================= Float_Parse ================= */ qboolean Float_Parse(char **p, float *f) { char *token; token = COM_ParseExt(p, qfalse); if (token && token[0] != 0) { *f = atof(token); return qtrue; } else { return qfalse; } } /* ================= PC_Float_Parse ================= */ qboolean PC_Float_Parse(int handle, float *f) { pc_token_t token; int negative = qfalse; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (token.string[0] == '-') { if (!trap_PC_ReadToken(handle, &token)) return qfalse; negative = qtrue; } if (token.type != TT_NUMBER) { PC_SourceError(handle, "expected float but found %s\n", token.string); return qfalse; } if (negative) *f = -token.floatvalue; else *f = token.floatvalue; return qtrue; } /* ================= Color_Parse ================= */ qboolean Color_Parse(char **p, vec4_t *c) { int i; float f; for (i = 0; i < 4; i++) { if (!Float_Parse(p, &f)) { return qfalse; } (*c)[i] = f; } return qtrue; } /* ================= PC_Color_Parse ================= */ qboolean PC_Color_Parse(int handle, vec4_t *c) { int i; float f; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } (*c)[i] = f; } return qtrue; } /* ================= Int_Parse ================= */ qboolean Int_Parse(char **p, int *i) { char *token; token = COM_ParseExt(p, qfalse); if (token && token[0] != 0) { *i = atoi(token); return qtrue; } else { return qfalse; } } /* ================= PC_Int_Parse ================= */ qboolean PC_Int_Parse(int handle, int *i) { pc_token_t token; int negative = qfalse; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (token.string[0] == '-') { if (!trap_PC_ReadToken(handle, &token)) return qfalse; negative = qtrue; } if (token.type != TT_NUMBER) { PC_SourceError(handle, "expected integer but found %s\n", token.string); return qfalse; } *i = token.intvalue; if (negative) *i = - *i; return qtrue; } /* ================= Rect_Parse ================= */ qboolean Rect_Parse(char **p, rectDef_t *r) { if (Float_Parse(p, &r->x)) { if (Float_Parse(p, &r->y)) { if (Float_Parse(p, &r->w)) { if (Float_Parse(p, &r->h)) { return qtrue; } } } } return qfalse; } /* ================= PC_Rect_Parse ================= */ qboolean PC_Rect_Parse(int handle, rectDef_t *r) { if (PC_Float_Parse(handle, &r->x)) { if (PC_Float_Parse(handle, &r->y)) { if (PC_Float_Parse(handle, &r->w)) { if (PC_Float_Parse(handle, &r->h)) { return qtrue; } } } } return qfalse; } /* ================= String_Parse ================= */ qboolean String_Parse(char **p, const char **out) { char *token; token = COM_ParseExt(p, qfalse); if (token && token[0] != 0) { *(out) = String_Alloc(token); return qtrue; } return qfalse; } /* ================= PC_String_Parse ================= */ qboolean PC_String_Parse(int handle, const char **out) { pc_token_t token; if (!trap_PC_ReadToken(handle, &token)) return qfalse; *(out) = String_Alloc(token.string); return qtrue; } /* ================= PC_Script_Parse ================= */ qboolean PC_Script_Parse(int handle, const char **out) { char script[1024]; pc_token_t token; memset(script, 0, sizeof(script)); // scripts start with { and have ; separated command lists.. commands are command, arg.. // basically we want everything between the { } as it will be interpreted at run time if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "{") != 0) { return qfalse; } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "}") == 0) { *out = String_Alloc(script); return qtrue; } if (token.string[1] != '\0') { Q_strcat(script, 1024, va("\"%s\"", token.string)); } else { Q_strcat(script, 1024, token.string); } Q_strcat(script, 1024, " "); } return qfalse; // bk001105 - LCC missing return value } // display, window, menu, item code // /* ================== Init_Display Initializes the display with a structure to all the drawing routines ================== */ void Init_Display(displayContextDef_t *dc) { DC = dc; } // type and style painting void GradientBar_Paint(rectDef_t *rect, vec4_t color) { // gradient bar takes two paints DC->setColor( color ); DC->drawHandlePic(rect->x, rect->y, rect->w, rect->h, DC->Assets.gradientBar); DC->setColor( NULL ); } /* ================== Window_Init Initializes a window structure ( windowDef_t ) with defaults ================== */ void Window_Init(Window *w) { memset(w, 0, sizeof(windowDef_t)); w->borderSize = 1; w->foreColor[0] = w->foreColor[1] = w->foreColor[2] = w->foreColor[3] = 1.0; w->cinematic = -1; } void Fade(int *flags, float *f, float clamp, int *nextTime, int offsetTime, qboolean bFlags, float fadeAmount) { if (*flags & (WINDOW_FADINGOUT | WINDOW_FADINGIN)) { if (DC->realTime > *nextTime) { *nextTime = DC->realTime + offsetTime; if (*flags & WINDOW_FADINGOUT) { *f -= fadeAmount; if (bFlags && *f <= 0.0) { *flags &= ~(WINDOW_FADINGOUT | WINDOW_VISIBLE); } } else { *f += fadeAmount; if (*f >= clamp) { *f = clamp; if (bFlags) { *flags &= ~WINDOW_FADINGIN; } } } } } } void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) { //float bordersize = 0; vec4_t color; rectDef_t fillRect = w->rect; color[0] = color[1] = color[2] = color[3] = 1; if (debugMode) { DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, 1, color); } if (w == NULL || (w->style == 0 && w->border == 0)) { return; } if (w->border != 0) { fillRect.x += w->borderSize; fillRect.y += w->borderSize; fillRect.w -= w->borderSize + 1; fillRect.h -= w->borderSize + 1; } if (w->style == WINDOW_STYLE_FILLED) { // box, but possible a shader that needs filled if (w->background) { Fade(&w->flags, &w->backColor[3], fadeClamp, &w->nextTime, fadeCycle, qtrue, fadeAmount); DC->setColor(w->backColor); DC->drawHandlePic(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->background); DC->setColor(NULL); } else { DC->fillRect(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->backColor); } } else if (w->style == WINDOW_STYLE_GRADIENT) { GradientBar_Paint(&fillRect, w->backColor); // gradient bar } else if (w->style == WINDOW_STYLE_SHADER) { if (w->flags & WINDOW_FORECOLORSET) { DC->setColor(w->foreColor); } DC->drawHandlePic(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->background); DC->setColor(NULL); } else if (w->style == WINDOW_STYLE_TEAMCOLOR) { if (DC->getTeamColor) { DC->getTeamColor(&color); DC->fillRect(fillRect.x, fillRect.y, fillRect.w, fillRect.h, color); } } else if (w->style == WINDOW_STYLE_CINEMATIC) { if (w->cinematic == -1) { w->cinematic = DC->playCinematic(w->cinematicName, fillRect.x, fillRect.y, fillRect.w, fillRect.h); if (w->cinematic == -1) { w->cinematic = -2; } } if (w->cinematic >= 0) { DC->runCinematicFrame(w->cinematic); DC->drawCinematic(w->cinematic, fillRect.x, fillRect.y, fillRect.w, fillRect.h); } } if (w->border == WINDOW_BORDER_FULL) { // full // HACK HACK HACK if (w->style == WINDOW_STYLE_TEAMCOLOR) { if (color[0] > 0) { // red color[0] = 1; color[1] = color[2] = .5; } else { color[2] = 1; color[0] = color[1] = .5; } color[3] = 1; DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize, color); } else { DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize, w->borderColor); } } else if (w->border == WINDOW_BORDER_HORZ) { // top/bottom DC->setColor(w->borderColor); DC->drawTopBottom(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize); DC->setColor( NULL ); } else if (w->border == WINDOW_BORDER_VERT) { // left right DC->setColor(w->borderColor); DC->drawSides(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize); DC->setColor( NULL ); } else if (w->border == WINDOW_BORDER_KCGRADIENT) { // this is just two gradient bars along each horz edge rectDef_t r = w->rect; r.h = w->borderSize; GradientBar_Paint(&r, w->borderColor); r.y = w->rect.y + w->rect.h - 1; GradientBar_Paint(&r, w->borderColor); } } void Item_SetScreenCoords(itemDef_t *item, float x, float y) { if (item == NULL) { return; } if (item->window.border != 0) { x += item->window.borderSize; y += item->window.borderSize; } item->window.rect.x = x + item->window.rectClient.x; item->window.rect.y = y + item->window.rectClient.y; item->window.rect.w = item->window.rectClient.w; item->window.rect.h = item->window.rectClient.h; // force the text rects to recompute item->textRect.w = 0; item->textRect.h = 0; } // FIXME: consolidate this with nearby stuff void Item_UpdatePosition(itemDef_t *item) { float x, y; menuDef_t *menu; if (item == NULL || item->parent == NULL) { return; } menu = item->parent; x = menu->window.rect.x; y = menu->window.rect.y; if (menu->window.border != 0) { x += menu->window.borderSize; y += menu->window.borderSize; } Item_SetScreenCoords(item, x, y); } // menus void Menu_UpdatePosition(menuDef_t *menu) { int i; float x, y; if (menu == NULL) { return; } x = menu->window.rect.x; y = menu->window.rect.y; if (menu->window.border != 0) { x += menu->window.borderSize; y += menu->window.borderSize; } for (i = 0; i < menu->itemCount; i++) { Item_SetScreenCoords(menu->items[i], x, y); } } void Menu_PostParse(menuDef_t *menu) { if (menu == NULL) { return; } if (menu->fullScreen) { menu->window.rect.x = 0; menu->window.rect.y = 0; menu->window.rect.w = 640; menu->window.rect.h = 480; } Menu_UpdatePosition(menu); } itemDef_t *Menu_ClearFocus(menuDef_t *menu) { int i; itemDef_t *ret = NULL; if (menu == NULL) { return NULL; } for (i = 0; i < menu->itemCount; i++) { if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { ret = menu->items[i]; } menu->items[i]->window.flags &= ~WINDOW_HASFOCUS; if (menu->items[i]->leaveFocus) { Item_RunScript(menu->items[i], menu->items[i]->leaveFocus); } } return ret; } qboolean IsVisible(int flags) { return (flags & WINDOW_VISIBLE && !(flags & WINDOW_FADINGOUT)); } qboolean Rect_ContainsPoint(rectDef_t *rect, float x, float y) { if (rect) { if (x > rect->x && x < rect->x + rect->w && y > rect->y && y < rect->y + rect->h) { return qtrue; } } return qfalse; } int Menu_ItemsMatchingGroup(menuDef_t *menu, const char *name) { int i; int count = 0; for (i = 0; i < menu->itemCount; i++) { if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) { count++; } } return count; } itemDef_t *Menu_GetMatchingItemByNumber(menuDef_t *menu, int index, const char *name) { int i; int count = 0; for (i = 0; i < menu->itemCount; i++) { if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) { if (count == index) { return menu->items[i]; } count++; } } return NULL; } void Script_SetColor(itemDef_t *item, char **args) { const char *name; int i; float f; vec4_t *out; // expecting type of color to set and 4 args for the color if (String_Parse(args, &name)) { out = NULL; if (Q_stricmp(name, "backcolor") == 0) { out = &item->window.backColor; item->window.flags |= WINDOW_BACKCOLORSET; } else if (Q_stricmp(name, "forecolor") == 0) { out = &item->window.foreColor; item->window.flags |= WINDOW_FORECOLORSET; } else if (Q_stricmp(name, "bordercolor") == 0) { out = &item->window.borderColor; } if (out) { for (i = 0; i < 4; i++) { if (!Float_Parse(args, &f)) { return; } (*out)[i] = f; } } } } void Script_SetAsset(itemDef_t *item, char **args) { const char *name; // expecting name to set asset to if (String_Parse(args, &name)) { // check for a model if (item->type == ITEM_TYPE_MODEL) { } } } void Script_SetBackground(itemDef_t *item, char **args) { const char *name; // expecting name to set asset to if (String_Parse(args, &name)) { item->window.background = DC->registerShaderNoMip(name); } } itemDef_t *Menu_FindItemByName(menuDef_t *menu, const char *p) { int i; if (menu == NULL || p == NULL) { return NULL; } for (i = 0; i < menu->itemCount; i++) { if (Q_stricmp(p, menu->items[i]->window.name) == 0) { return menu->items[i]; } } return NULL; } void Script_SetTeamColor(itemDef_t *item, char **args) { if (DC->getTeamColor) { int i; vec4_t color; DC->getTeamColor(&color); for (i = 0; i < 4; i++) { item->window.backColor[i] = color[i]; } } } void Script_SetItemColor(itemDef_t *item, char **args) { const char *itemname; const char *name; vec4_t color; int i; vec4_t *out; // expecting type of color to set and 4 args for the color if (String_Parse(args, &itemname) && String_Parse(args, &name)) { itemDef_t *item2; int j; int count = Menu_ItemsMatchingGroup(item->parent, itemname); if (!Color_Parse(args, &color)) { return; } for (j = 0; j < count; j++) { item2 = Menu_GetMatchingItemByNumber(item->parent, j, itemname); if (item2 != NULL) { out = NULL; if (Q_stricmp(name, "backcolor") == 0) { out = &item2->window.backColor; } else if (Q_stricmp(name, "forecolor") == 0) { out = &item2->window.foreColor; item2->window.flags |= WINDOW_FORECOLORSET; } else if (Q_stricmp(name, "bordercolor") == 0) { out = &item2->window.borderColor; } if (out) { for (i = 0; i < 4; i++) { (*out)[i] = color[i]; } } } } } } void Menu_ShowItemByName(menuDef_t *menu, const char *p, qboolean bShow) { itemDef_t *item; int i; int count = Menu_ItemsMatchingGroup(menu, p); for (i = 0; i < count; i++) { item = Menu_GetMatchingItemByNumber(menu, i, p); if (item != NULL) { if (bShow) { item->window.flags |= WINDOW_VISIBLE; } else { item->window.flags &= ~WINDOW_VISIBLE; // stop cinematics playing in the window if (item->window.cinematic >= 0) { DC->stopCinematic(item->window.cinematic); item->window.cinematic = -1; } } } } } void Menu_FadeItemByName(menuDef_t *menu, const char *p, qboolean fadeOut) { itemDef_t *item; int i; int count = Menu_ItemsMatchingGroup(menu, p); for (i = 0; i < count; i++) { item = Menu_GetMatchingItemByNumber(menu, i, p); if (item != NULL) { if (fadeOut) { item->window.flags |= (WINDOW_FADINGOUT | WINDOW_VISIBLE); item->window.flags &= ~WINDOW_FADINGIN; } else { item->window.flags |= (WINDOW_VISIBLE | WINDOW_FADINGIN); item->window.flags &= ~WINDOW_FADINGOUT; } } } } menuDef_t *Menus_FindByName(const char *p) { int i; for (i = 0; i < menuCount; i++) { if (Q_stricmp(Menus[i].window.name, p) == 0) { return &Menus[i]; } } return NULL; } void Menus_ShowByName(const char *p) { menuDef_t *menu = Menus_FindByName(p); if (menu) { Menus_Activate(menu); } } void Menus_OpenByName(const char *p) { Menus_ActivateByName(p); } static void Menu_RunCloseScript(menuDef_t *menu) { if (menu && menu->window.flags & WINDOW_VISIBLE && menu->onClose) { itemDef_t item; item.parent = menu; Item_RunScript(&item, menu->onClose); } } void Menus_CloseByName(const char *p) { menuDef_t *menu = Menus_FindByName(p); if (menu != NULL) { Menu_RunCloseScript(menu); menu->window.flags &= ~(WINDOW_VISIBLE | WINDOW_HASFOCUS); } } void Menus_CloseAll(void) { int i; for (i = 0; i < menuCount; i++) { Menu_RunCloseScript(&Menus[i]); Menus[i].window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); } } void Script_Show(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menu_ShowItemByName(item->parent, name, qtrue); } } void Script_Hide(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menu_ShowItemByName(item->parent, name, qfalse); } } void Script_FadeIn(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menu_FadeItemByName(item->parent, name, qfalse); } } void Script_FadeOut(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menu_FadeItemByName(item->parent, name, qtrue); } } void Script_Open(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menus_OpenByName(name); } } void Script_ConditionalOpen(itemDef_t *item, char **args) { const char *cvar; const char *name1; const char *name2; float val; if ( String_Parse(args, &cvar) && String_Parse(args, &name1) && String_Parse(args, &name2) ) { val = DC->getCVarValue( cvar ); if ( val == 0.f ) { Menus_OpenByName(name2); } else { Menus_OpenByName(name1); } } } void Script_Close(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { Menus_CloseByName(name); } } void Menu_TransitionItemByName(menuDef_t *menu, const char *p, rectDef_t rectFrom, rectDef_t rectTo, int time, float amt) { itemDef_t *item; int i; int count = Menu_ItemsMatchingGroup(menu, p); for (i = 0; i < count; i++) { item = Menu_GetMatchingItemByNumber(menu, i, p); if (item != NULL) { item->window.flags |= (WINDOW_INTRANSITION | WINDOW_VISIBLE); item->window.offsetTime = time; memcpy(&item->window.rectClient, &rectFrom, sizeof(rectDef_t)); memcpy(&item->window.rectEffects, &rectTo, sizeof(rectDef_t)); item->window.rectEffects2.x = abs(rectTo.x - rectFrom.x) / amt; item->window.rectEffects2.y = abs(rectTo.y - rectFrom.y) / amt; item->window.rectEffects2.w = abs(rectTo.w - rectFrom.w) / amt; item->window.rectEffects2.h = abs(rectTo.h - rectFrom.h) / amt; Item_UpdatePosition(item); } } } void Script_Transition(itemDef_t *item, char **args) { const char *name; rectDef_t rectFrom, rectTo; int time; float amt; if (String_Parse(args, &name)) { if ( Rect_Parse(args, &rectFrom) && Rect_Parse(args, &rectTo) && Int_Parse(args, &time) && Float_Parse(args, &amt)) { Menu_TransitionItemByName(item->parent, name, rectFrom, rectTo, time, amt); } } } void Menu_OrbitItemByName(menuDef_t *menu, const char *p, float x, float y, float cx, float cy, int time) { itemDef_t *item; int i; int count = Menu_ItemsMatchingGroup(menu, p); for (i = 0; i < count; i++) { item = Menu_GetMatchingItemByNumber(menu, i, p); if (item != NULL) { item->window.flags |= (WINDOW_ORBITING | WINDOW_VISIBLE); item->window.offsetTime = time; item->window.rectEffects.x = cx; item->window.rectEffects.y = cy; item->window.rectClient.x = x; item->window.rectClient.y = y; Item_UpdatePosition(item); } } } void Script_Orbit(itemDef_t *item, char **args) { const char *name; float cx, cy, x, y; int time; if (String_Parse(args, &name)) { if ( Float_Parse(args, &x) && Float_Parse(args, &y) && Float_Parse(args, &cx) && Float_Parse(args, &cy) && Int_Parse(args, &time) ) { Menu_OrbitItemByName(item->parent, name, x, y, cx, cy, time); } } } void Script_SetFocus(itemDef_t *item, char **args) { const char *name; itemDef_t *focusItem; if (String_Parse(args, &name)) { focusItem = Menu_FindItemByName(item->parent, name); if (focusItem && !(focusItem->window.flags & WINDOW_DECORATION) && !(focusItem->window.flags & WINDOW_HASFOCUS)) { Menu_ClearFocus(item->parent); focusItem->window.flags |= WINDOW_HASFOCUS; if (focusItem->onFocus) { Item_RunScript(focusItem, focusItem->onFocus); } if (DC->Assets.itemFocusSound) { DC->startLocalSound( DC->Assets.itemFocusSound, CHAN_LOCAL_SOUND ); } } } } void Script_SetPlayerModel(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { DC->setCVar("team_model", name); } } void Script_SetPlayerHead(itemDef_t *item, char **args) { const char *name; if (String_Parse(args, &name)) { DC->setCVar("team_headmodel", name); } } void Script_SetCvar(itemDef_t *item, char **args) { const char *cvar, *val; if (String_Parse(args, &cvar) && String_Parse(args, &val)) { DC->setCVar(cvar, val); } } void Script_Exec(itemDef_t *item, char **args) { const char *val; if (String_Parse(args, &val)) { DC->executeText(EXEC_APPEND, va("%s ; ", val)); } } void Script_Play(itemDef_t *item, char **args) { const char *val; if (String_Parse(args, &val)) { DC->startLocalSound(DC->registerSound(val, qfalse), CHAN_LOCAL_SOUND); } } void Script_playLooped(itemDef_t *item, char **args) { const char *val; if (String_Parse(args, &val)) { DC->stopBackgroundTrack(); DC->startBackgroundTrack(val, val); } } commandDef_t commandList[] = { {"fadein", &Script_FadeIn}, // group/name {"fadeout", &Script_FadeOut}, // group/name {"show", &Script_Show}, // group/name {"hide", &Script_Hide}, // group/name {"setcolor", &Script_SetColor}, // works on this {"open", &Script_Open}, // menu {"conditionalopen", &Script_ConditionalOpen}, // menu {"close", &Script_Close}, // menu {"setasset", &Script_SetAsset}, // works on this {"setbackground", &Script_SetBackground}, // works on this {"setitemcolor", &Script_SetItemColor}, // group/name {"setteamcolor", &Script_SetTeamColor}, // sets this background color to team color {"setfocus", &Script_SetFocus}, // sets this background color to team color {"setplayermodel", &Script_SetPlayerModel}, // sets this background color to team color {"setplayerhead", &Script_SetPlayerHead}, // sets this background color to team color {"transition", &Script_Transition}, // group/name {"setcvar", &Script_SetCvar}, // group/name {"exec", &Script_Exec}, // group/name {"play", &Script_Play}, // group/name {"playlooped", &Script_playLooped}, // group/name {"orbit", &Script_Orbit} // group/name }; int scriptCommandCount = sizeof(commandList) / sizeof(commandDef_t); void Item_RunScript(itemDef_t *item, const char *s) { char script[1024], *p; int i; qboolean bRan; memset(script, 0, sizeof(script)); if (item && s && s[0]) { Q_strcat(script, 1024, s); p = script; while (1) { const char *command; // expect command then arguments, ; ends command, NULL ends script if (!String_Parse(&p, &command)) { return; } if (command[0] == ';' && command[1] == '\0') { continue; } bRan = qfalse; for (i = 0; i < scriptCommandCount; i++) { if (Q_stricmp(command, commandList[i].name) == 0) { (commandList[i].handler(item, &p)); bRan = qtrue; break; } } // not in our auto list, pass to handler if (!bRan) { DC->runScript(&p); } } } } qboolean Item_EnableShowViaCvar(itemDef_t *item, int flag) { char script[1024], *p; memset(script, 0, sizeof(script)); if (item && item->enableCvar && *item->enableCvar && item->cvarTest && *item->cvarTest) { char buff[1024]; DC->getCVarString(item->cvarTest, buff, sizeof(buff)); Q_strcat(script, 1024, item->enableCvar); p = script; while (1) { const char *val; // expect value then ; or NULL, NULL ends list if (!String_Parse(&p, &val)) { return (item->cvarFlags & flag) ? qfalse : qtrue; } if (val[0] == ';' && val[1] == '\0') { continue; } // enable it if any of the values are true if (item->cvarFlags & flag) { if (Q_stricmp(buff, val) == 0) { return qtrue; } } else { // disable it if any of the values are true if (Q_stricmp(buff, val) == 0) { return qfalse; } } } return (item->cvarFlags & flag) ? qfalse : qtrue; } return qtrue; } // will optionaly set focus to this item qboolean Item_SetFocus(itemDef_t *item, float x, float y) { int i; itemDef_t *oldFocus; sfxHandle_t *sfx = &DC->Assets.itemFocusSound; qboolean playSound = qfalse; menuDef_t *parent; // bk001206: = (menuDef_t*)item->parent; // sanity check, non-null, not a decoration and does not already have the focus if (item == NULL || item->window.flags & WINDOW_DECORATION || item->window.flags & WINDOW_HASFOCUS || !(item->window.flags & WINDOW_VISIBLE)) { return qfalse; } // bk001206 - this can be NULL. parent = (menuDef_t*)item->parent; // items can be enabled and disabled based on cvars if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { return qfalse; } if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(item, CVAR_SHOW)) { return qfalse; } oldFocus = Menu_ClearFocus(item->parent); if (item->type == ITEM_TYPE_TEXT) { rectDef_t r; r = item->textRect; r.y -= r.h; if (Rect_ContainsPoint(&r, x, y)) { item->window.flags |= WINDOW_HASFOCUS; if (item->focusSound) { sfx = &item->focusSound; } playSound = qtrue; } else { if (oldFocus) { oldFocus->window.flags |= WINDOW_HASFOCUS; if (oldFocus->onFocus) { Item_RunScript(oldFocus, oldFocus->onFocus); } } } } else { item->window.flags |= WINDOW_HASFOCUS; if (item->onFocus) { Item_RunScript(item, item->onFocus); } if (item->focusSound) { sfx = &item->focusSound; } playSound = qtrue; } if (playSound && sfx) { DC->startLocalSound( *sfx, CHAN_LOCAL_SOUND ); } for (i = 0; i < parent->itemCount; i++) { if (parent->items[i] == item) { parent->cursorItem = i; break; } } return qtrue; } int Item_ListBox_MaxScroll(itemDef_t *item) { listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; int count = DC->feederCount(item->special); int max; if (item->window.flags & WINDOW_HORIZONTAL) { max = count - (item->window.rect.w / listPtr->elementWidth) + 1; } else { max = count - (item->window.rect.h / listPtr->elementHeight) + 1; } if (max < 0) { return 0; } return max; } int Item_ListBox_ThumbPosition(itemDef_t *item) { float max, pos, size; listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; max = Item_ListBox_MaxScroll(item); if (item->window.flags & WINDOW_HORIZONTAL) { size = item->window.rect.w - (SCROLLBAR_SIZE * 2) - 2; if (max > 0) { pos = (size-SCROLLBAR_SIZE) / (float) max; } else { pos = 0; } pos *= listPtr->startPos; return item->window.rect.x + 1 + SCROLLBAR_SIZE + pos; } else { size = item->window.rect.h - (SCROLLBAR_SIZE * 2) - 2; if (max > 0) { pos = (size-SCROLLBAR_SIZE) / (float) max; } else { pos = 0; } pos *= listPtr->startPos; return item->window.rect.y + 1 + SCROLLBAR_SIZE + pos; } } int Item_ListBox_ThumbDrawPosition(itemDef_t *item) { int min, max; if (itemCapture == item) { if (item->window.flags & WINDOW_HORIZONTAL) { min = item->window.rect.x + SCROLLBAR_SIZE + 1; max = item->window.rect.x + item->window.rect.w - 2*SCROLLBAR_SIZE - 1; if (DC->cursorx >= min + SCROLLBAR_SIZE/2 && DC->cursorx <= max + SCROLLBAR_SIZE/2) { return DC->cursorx - SCROLLBAR_SIZE/2; } else { return Item_ListBox_ThumbPosition(item); } } else { min = item->window.rect.y + SCROLLBAR_SIZE + 1; max = item->window.rect.y + item->window.rect.h - 2*SCROLLBAR_SIZE - 1; if (DC->cursory >= min + SCROLLBAR_SIZE/2 && DC->cursory <= max + SCROLLBAR_SIZE/2) { return DC->cursory - SCROLLBAR_SIZE/2; } else { return Item_ListBox_ThumbPosition(item); } } } else { return Item_ListBox_ThumbPosition(item); } } float Item_Slider_ThumbPosition(itemDef_t *item) { float value, range, x; editFieldDef_t *editDef = item->typeData; if (item->text) { x = item->textRect.x + item->textRect.w + 8; } else { x = item->window.rect.x; } if (editDef == NULL && item->cvar) { return x; } value = DC->getCVarValue(item->cvar); if (value < editDef->minVal) { value = editDef->minVal; } else if (value > editDef->maxVal) { value = editDef->maxVal; } range = editDef->maxVal - editDef->minVal; value -= editDef->minVal; value /= range; //value /= (editDef->maxVal - editDef->minVal); value *= SLIDER_WIDTH; x += value; // vm fuckage //x = x + (((float)value / editDef->maxVal) * SLIDER_WIDTH); return x; } int Item_Slider_OverSlider(itemDef_t *item, float x, float y) { rectDef_t r; r.x = Item_Slider_ThumbPosition(item) - (SLIDER_THUMB_WIDTH / 2); r.y = item->window.rect.y - 2; r.w = SLIDER_THUMB_WIDTH; r.h = SLIDER_THUMB_HEIGHT; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_THUMB; } return 0; } int Item_ListBox_OverLB(itemDef_t *item, float x, float y) { rectDef_t r; listBoxDef_t *listPtr; int thumbstart; int count; count = DC->feederCount(item->special); listPtr = (listBoxDef_t*)item->typeData; if (item->window.flags & WINDOW_HORIZONTAL) { // check if on left arrow r.x = item->window.rect.x; r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; r.h = r.w = SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_LEFTARROW; } // check if on right arrow r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_RIGHTARROW; } // check if on thumb thumbstart = Item_ListBox_ThumbPosition(item); r.x = thumbstart; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_THUMB; } r.x = item->window.rect.x + SCROLLBAR_SIZE; r.w = thumbstart - r.x; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_PGUP; } r.x = thumbstart + SCROLLBAR_SIZE; r.w = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_PGDN; } } else { r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; r.y = item->window.rect.y; r.h = r.w = SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_LEFTARROW; } r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_RIGHTARROW; } thumbstart = Item_ListBox_ThumbPosition(item); r.y = thumbstart; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_THUMB; } r.y = item->window.rect.y + SCROLLBAR_SIZE; r.h = thumbstart - r.y; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_PGUP; } r.y = thumbstart + SCROLLBAR_SIZE; r.h = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_PGDN; } } return 0; } void Item_ListBox_MouseEnter(itemDef_t *item, float x, float y) { rectDef_t r; listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; item->window.flags &= ~(WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN); item->window.flags |= Item_ListBox_OverLB(item, x, y); if (item->window.flags & WINDOW_HORIZONTAL) { if (!(item->window.flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN))) { // check for selection hit as we have exausted buttons and thumb if (listPtr->elementStyle == LISTBOX_IMAGE) { r.x = item->window.rect.x; r.y = item->window.rect.y; r.h = item->window.rect.h - SCROLLBAR_SIZE; r.w = item->window.rect.w - listPtr->drawPadding; if (Rect_ContainsPoint(&r, x, y)) { listPtr->cursorPos = (int)((x - r.x) / listPtr->elementWidth) + listPtr->startPos; if (listPtr->cursorPos >= listPtr->endPos) { listPtr->cursorPos = listPtr->endPos; } } } else { // text hit.. } } } else if (!(item->window.flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN))) { r.x = item->window.rect.x; r.y = item->window.rect.y; r.w = item->window.rect.w - SCROLLBAR_SIZE; r.h = item->window.rect.h - listPtr->drawPadding; if (Rect_ContainsPoint(&r, x, y)) { listPtr->cursorPos = (int)((y - 2 - r.y) / listPtr->elementHeight) + listPtr->startPos; if (listPtr->cursorPos > listPtr->endPos) { listPtr->cursorPos = listPtr->endPos; } } } } void Item_MouseEnter(itemDef_t *item, float x, float y) { rectDef_t r; if (item) { r = item->textRect; r.y -= r.h; // in the text rect? // items can be enabled and disabled based on cvars if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { return; } if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(item, CVAR_SHOW)) { return; } if (Rect_ContainsPoint(&r, x, y)) { if (!(item->window.flags & WINDOW_MOUSEOVERTEXT)) { Item_RunScript(item, item->mouseEnterText); item->window.flags |= WINDOW_MOUSEOVERTEXT; } if (!(item->window.flags & WINDOW_MOUSEOVER)) { Item_RunScript(item, item->mouseEnter); item->window.flags |= WINDOW_MOUSEOVER; } } else { // not in the text rect if (item->window.flags & WINDOW_MOUSEOVERTEXT) { // if we were Item_RunScript(item, item->mouseExitText); item->window.flags &= ~WINDOW_MOUSEOVERTEXT; } if (!(item->window.flags & WINDOW_MOUSEOVER)) { Item_RunScript(item, item->mouseEnter); item->window.flags |= WINDOW_MOUSEOVER; } if (item->type == ITEM_TYPE_LISTBOX) { Item_ListBox_MouseEnter(item, x, y); } } } } void Item_MouseLeave(itemDef_t *item) { if (item) { if (item->window.flags & WINDOW_MOUSEOVERTEXT) { Item_RunScript(item, item->mouseExitText); item->window.flags &= ~WINDOW_MOUSEOVERTEXT; } Item_RunScript(item, item->mouseExit); item->window.flags &= ~(WINDOW_LB_RIGHTARROW | WINDOW_LB_LEFTARROW); } } itemDef_t *Menu_HitTest(menuDef_t *menu, float x, float y) { int i; for (i = 0; i < menu->itemCount; i++) { if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { return menu->items[i]; } } return NULL; } void Item_SetMouseOver(itemDef_t *item, qboolean focus) { if (item) { if (focus) { item->window.flags |= WINDOW_MOUSEOVER; } else { item->window.flags &= ~WINDOW_MOUSEOVER; } } } qboolean Item_OwnerDraw_HandleKey(itemDef_t *item, int key) { if (item && DC->ownerDrawHandleKey) { return DC->ownerDrawHandleKey(item->window.ownerDraw, item->window.ownerDrawFlags, &item->special, key); } return qfalse; } qboolean Item_ListBox_HandleKey(itemDef_t *item, int key, qboolean down, qboolean force) { listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; int count = DC->feederCount(item->special); int max, viewmax; if (force || (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS)) { max = Item_ListBox_MaxScroll(item); if (item->window.flags & WINDOW_HORIZONTAL) { viewmax = (item->window.rect.w / listPtr->elementWidth); if ( key == K_LEFTARROW ) { if (!listPtr->notselectable) { listPtr->cursorPos--; if (listPtr->cursorPos < 0) { listPtr->cursorPos = 0; } if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos--; if (listPtr->startPos < 0) listPtr->startPos = 0; } return qtrue; } if ( key == K_RIGHTARROW ) { if (!listPtr->notselectable) { listPtr->cursorPos++; if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= count) { listPtr->cursorPos = count-1; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos++; if (listPtr->startPos >= count) listPtr->startPos = count-1; } return qtrue; } } else { viewmax = (item->window.rect.h / listPtr->elementHeight); if ( key == K_UPARROW ) { if (!listPtr->notselectable) { listPtr->cursorPos--; if (listPtr->cursorPos < 0) { listPtr->cursorPos = 0; } if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos--; if (listPtr->startPos < 0) listPtr->startPos = 0; } return qtrue; } if ( key == K_DOWNARROW ) { if (!listPtr->notselectable) { listPtr->cursorPos++; if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= count) { listPtr->cursorPos = count-1; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos++; if (listPtr->startPos > max) listPtr->startPos = max; } return qtrue; } } // mouse hit if (key == K_MOUSE1 || key == K_MOUSE2) { if (item->window.flags & WINDOW_LB_LEFTARROW) { listPtr->startPos--; if (listPtr->startPos < 0) { listPtr->startPos = 0; } } else if (item->window.flags & WINDOW_LB_RIGHTARROW) { // one down listPtr->startPos++; if (listPtr->startPos > max) { listPtr->startPos = max; } } else if (item->window.flags & WINDOW_LB_PGUP) { // page up listPtr->startPos -= viewmax; if (listPtr->startPos < 0) { listPtr->startPos = 0; } } else if (item->window.flags & WINDOW_LB_PGDN) { // page down listPtr->startPos += viewmax; if (listPtr->startPos > max) { listPtr->startPos = max; } } else if (item->window.flags & WINDOW_LB_THUMB) { // Display_SetCaptureItem(item); } else { // select an item if (DC->realTime < lastListBoxClickTime && listPtr->doubleClick) { Item_RunScript(item, listPtr->doubleClick); } lastListBoxClickTime = DC->realTime + DOUBLE_CLICK_DELAY; if (item->cursorPos != listPtr->cursorPos) { item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } } return qtrue; } if ( key == K_HOME ) { // home listPtr->startPos = 0; return qtrue; } if ( key == K_END ) { // end listPtr->startPos = max; return qtrue; } if (key == K_PGUP ) { // page up if (!listPtr->notselectable) { listPtr->cursorPos -= viewmax; if (listPtr->cursorPos < 0) { listPtr->cursorPos = 0; } if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos -= viewmax; if (listPtr->startPos < 0) { listPtr->startPos = 0; } } return qtrue; } if ( key == K_PGDN ) { // page down if (!listPtr->notselectable) { listPtr->cursorPos += viewmax; if (listPtr->cursorPos < listPtr->startPos) { listPtr->startPos = listPtr->cursorPos; } if (listPtr->cursorPos >= count) { listPtr->cursorPos = count-1; } if (listPtr->cursorPos >= listPtr->startPos + viewmax) { listPtr->startPos = listPtr->cursorPos - viewmax + 1; } item->cursorPos = listPtr->cursorPos; DC->feederSelection(item->special, item->cursorPos); } else { listPtr->startPos += viewmax; if (listPtr->startPos > max) { listPtr->startPos = max; } } return qtrue; } } return qfalse; } qboolean Item_YesNo_HandleKey(itemDef_t *item, int key) { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS && item->cvar) { if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { DC->setCVar(item->cvar, va("%i", !DC->getCVarValue(item->cvar))); return qtrue; } } return qfalse; } int Item_Multi_CountSettings(itemDef_t *item) { multiDef_t *multiPtr = (multiDef_t*)item->typeData; if (multiPtr == NULL) { return 0; } return multiPtr->count; } int Item_Multi_FindCvarByValue(itemDef_t *item) { char buff[1024]; float value = 0; int i; multiDef_t *multiPtr = (multiDef_t*)item->typeData; if (multiPtr) { if (multiPtr->strDef) { DC->getCVarString(item->cvar, buff, sizeof(buff)); } else { value = DC->getCVarValue(item->cvar); } for (i = 0; i < multiPtr->count; i++) { if (multiPtr->strDef) { if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) { return i; } } else { if (multiPtr->cvarValue[i] == value) { return i; } } } } return 0; } const char *Item_Multi_Setting(itemDef_t *item) { char buff[1024]; float value = 0; int i; multiDef_t *multiPtr = (multiDef_t*)item->typeData; if (multiPtr) { if (multiPtr->strDef) { DC->getCVarString(item->cvar, buff, sizeof(buff)); } else { value = DC->getCVarValue(item->cvar); } for (i = 0; i < multiPtr->count; i++) { if (multiPtr->strDef) { if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) { return multiPtr->cvarList[i]; } } else { if (multiPtr->cvarValue[i] == value) { return multiPtr->cvarList[i]; } } } } return ""; } qboolean Item_Multi_HandleKey(itemDef_t *item, int key) { multiDef_t *multiPtr = (multiDef_t*)item->typeData; if (multiPtr) { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS && item->cvar) { if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { int current = Item_Multi_FindCvarByValue(item) + 1; int max = Item_Multi_CountSettings(item); if ( current < 0 || current >= max ) { current = 0; } if (multiPtr->strDef) { DC->setCVar(item->cvar, multiPtr->cvarStr[current]); } else { float value = multiPtr->cvarValue[current]; if (((float)((int) value)) == value) { DC->setCVar(item->cvar, va("%i", (int) value )); } else { DC->setCVar(item->cvar, va("%f", value )); } } return qtrue; } } } return qfalse; } qboolean Item_TextField_HandleKey(itemDef_t *item, int key) { char buff[1024]; int len; itemDef_t *newItem = NULL; editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; if (item->cvar) { memset(buff, 0, sizeof(buff)); DC->getCVarString(item->cvar, buff, sizeof(buff)); len = strlen(buff); if (editPtr->maxChars && len > editPtr->maxChars) { len = editPtr->maxChars; } if ( key & K_CHAR_FLAG ) { key &= ~K_CHAR_FLAG; if (key == 'h' - 'a' + 1 ) { // ctrl-h is backspace if ( item->cursorPos > 0 ) { memmove( &buff[item->cursorPos - 1], &buff[item->cursorPos], len + 1 - item->cursorPos); item->cursorPos--; if (item->cursorPos < editPtr->paintOffset) { editPtr->paintOffset--; } } DC->setCVar(item->cvar, buff); return qtrue; } // // ignore any non printable chars // if ( key < 32 || !item->cvar) { return qtrue; } if (item->type == ITEM_TYPE_NUMERICFIELD) { if (key < '0' || key > '9') { return qfalse; } } if (!DC->getOverstrikeMode()) { if (( len == MAX_EDITFIELD - 1 ) || (editPtr->maxChars && len >= editPtr->maxChars)) { return qtrue; } memmove( &buff[item->cursorPos + 1], &buff[item->cursorPos], len + 1 - item->cursorPos ); } else { if (editPtr->maxChars && item->cursorPos >= editPtr->maxChars) { return qtrue; } } buff[item->cursorPos] = key; DC->setCVar(item->cvar, buff); if (item->cursorPos < len + 1) { item->cursorPos++; if (editPtr->maxPaintChars && item->cursorPos > editPtr->maxPaintChars) { editPtr->paintOffset++; } } } else { if ( key == K_DEL ) { if ( item->cursorPos < len ) { memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos); DC->setCVar(item->cvar, buff); } return qtrue; } if ( key == K_RIGHTARROW ) { if (editPtr->maxPaintChars && item->cursorPos >= editPtr->maxPaintChars && item->cursorPos < len) { item->cursorPos++; editPtr->paintOffset++; return qtrue; } if (item->cursorPos < len) { item->cursorPos++; } return qtrue; } if ( key == K_LEFTARROW ) { if ( item->cursorPos > 0 ) { item->cursorPos--; } if (item->cursorPos < editPtr->paintOffset) { editPtr->paintOffset--; } return qtrue; } if ( key == K_HOME ) {// || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) { item->cursorPos = 0; editPtr->paintOffset = 0; return qtrue; } if ( key == K_END ) {// ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) { item->cursorPos = len; if(item->cursorPos > editPtr->maxPaintChars) { editPtr->paintOffset = len - editPtr->maxPaintChars; } return qtrue; } if ( key == K_INS ) { DC->setOverstrikeMode(!DC->getOverstrikeMode()); return qtrue; } } if (key == K_TAB || key == K_DOWNARROW ) { newItem = Menu_SetNextCursorItem(item->parent); if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { g_editItem = newItem; } } if (key == K_UPARROW ) { newItem = Menu_SetPrevCursorItem(item->parent); if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { g_editItem = newItem; } } if ( key == K_ENTER || key == K_KP_ENTER || key == K_ESCAPE) { return qfalse; } return qtrue; } return qfalse; } static void Scroll_ListBox_AutoFunc(void *p) { scrollInfo_t *si = (scrollInfo_t*)p; if (DC->realTime > si->nextScrollTime) { // need to scroll which is done by simulating a click to the item // this is done a bit sideways as the autoscroll "knows" that the item is a listbox // so it calls it directly Item_ListBox_HandleKey(si->item, si->scrollKey, qtrue, qfalse); si->nextScrollTime = DC->realTime + si->adjustValue; } if (DC->realTime > si->nextAdjustTime) { si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; if (si->adjustValue > SCROLL_TIME_FLOOR) { si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; } } } static void Scroll_ListBox_ThumbFunc(void *p) { scrollInfo_t *si = (scrollInfo_t*)p; rectDef_t r; int pos, max; listBoxDef_t *listPtr = (listBoxDef_t*)si->item->typeData; if (si->item->window.flags & WINDOW_HORIZONTAL) { if (DC->cursorx == si->xStart) { return; } r.x = si->item->window.rect.x + SCROLLBAR_SIZE + 1; r.y = si->item->window.rect.y + si->item->window.rect.h - SCROLLBAR_SIZE - 1; r.h = SCROLLBAR_SIZE; r.w = si->item->window.rect.w - (SCROLLBAR_SIZE*2) - 2; max = Item_ListBox_MaxScroll(si->item); // pos = (DC->cursorx - r.x - SCROLLBAR_SIZE/2) * max / (r.w - SCROLLBAR_SIZE); if (pos < 0) { pos = 0; } else if (pos > max) { pos = max; } listPtr->startPos = pos; si->xStart = DC->cursorx; } else if (DC->cursory != si->yStart) { r.x = si->item->window.rect.x + si->item->window.rect.w - SCROLLBAR_SIZE - 1; r.y = si->item->window.rect.y + SCROLLBAR_SIZE + 1; r.h = si->item->window.rect.h - (SCROLLBAR_SIZE*2) - 2; r.w = SCROLLBAR_SIZE; max = Item_ListBox_MaxScroll(si->item); // pos = (DC->cursory - r.y - SCROLLBAR_SIZE/2) * max / (r.h - SCROLLBAR_SIZE); if (pos < 0) { pos = 0; } else if (pos > max) { pos = max; } listPtr->startPos = pos; si->yStart = DC->cursory; } if (DC->realTime > si->nextScrollTime) { // need to scroll which is done by simulating a click to the item // this is done a bit sideways as the autoscroll "knows" that the item is a listbox // so it calls it directly Item_ListBox_HandleKey(si->item, si->scrollKey, qtrue, qfalse); si->nextScrollTime = DC->realTime + si->adjustValue; } if (DC->realTime > si->nextAdjustTime) { si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; if (si->adjustValue > SCROLL_TIME_FLOOR) { si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; } } } static void Scroll_Slider_ThumbFunc(void *p) { float x, value, cursorx; scrollInfo_t *si = (scrollInfo_t*)p; editFieldDef_t *editDef = si->item->typeData; if (si->item->text) { x = si->item->textRect.x + si->item->textRect.w + 8; } else { x = si->item->window.rect.x; } cursorx = DC->cursorx; if (cursorx < x) { cursorx = x; } else if (cursorx > x + SLIDER_WIDTH) { cursorx = x + SLIDER_WIDTH; } value = cursorx - x; value /= SLIDER_WIDTH; value *= (editDef->maxVal - editDef->minVal); value += editDef->minVal; DC->setCVar(si->item->cvar, va("%f", value)); } void Item_StartCapture(itemDef_t *item, int key) { int flags; switch (item->type) { case ITEM_TYPE_EDITFIELD: case ITEM_TYPE_NUMERICFIELD: case ITEM_TYPE_LISTBOX: { flags = Item_ListBox_OverLB(item, DC->cursorx, DC->cursory); if (flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW)) { scrollInfo.nextScrollTime = DC->realTime + SCROLL_TIME_START; scrollInfo.nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; scrollInfo.adjustValue = SCROLL_TIME_START; scrollInfo.scrollKey = key; scrollInfo.scrollDir = (flags & WINDOW_LB_LEFTARROW) ? qtrue : qfalse; scrollInfo.item = item; captureData = &scrollInfo; captureFunc = &Scroll_ListBox_AutoFunc; itemCapture = item; } else if (flags & WINDOW_LB_THUMB) { scrollInfo.scrollKey = key; scrollInfo.item = item; scrollInfo.xStart = DC->cursorx; scrollInfo.yStart = DC->cursory; captureData = &scrollInfo; captureFunc = &Scroll_ListBox_ThumbFunc; itemCapture = item; } break; } case ITEM_TYPE_SLIDER: { flags = Item_Slider_OverSlider(item, DC->cursorx, DC->cursory); if (flags & WINDOW_LB_THUMB) { scrollInfo.scrollKey = key; scrollInfo.item = item; scrollInfo.xStart = DC->cursorx; scrollInfo.yStart = DC->cursory; captureData = &scrollInfo; captureFunc = &Scroll_Slider_ThumbFunc; itemCapture = item; } break; } } } void Item_StopCapture(itemDef_t *item) { } qboolean Item_Slider_HandleKey(itemDef_t *item, int key, qboolean down) { float x, value, width, work; //DC->Print("slider handle key\n"); if (item->window.flags & WINDOW_HASFOCUS && item->cvar && Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { editFieldDef_t *editDef = item->typeData; if (editDef) { rectDef_t testRect; width = SLIDER_WIDTH; if (item->text) { x = item->textRect.x + item->textRect.w + 8; } else { x = item->window.rect.x; } testRect = item->window.rect; testRect.x = x; value = (float)SLIDER_THUMB_WIDTH / 2; testRect.x -= value; //DC->Print("slider x: %f\n", testRect.x); testRect.w = (SLIDER_WIDTH + (float)SLIDER_THUMB_WIDTH / 2); //DC->Print("slider w: %f\n", testRect.w); if (Rect_ContainsPoint(&testRect, DC->cursorx, DC->cursory)) { work = DC->cursorx - x; value = work / width; value *= (editDef->maxVal - editDef->minVal); // vm fuckage // value = (((float)(DC->cursorx - x)/ SLIDER_WIDTH) * (editDef->maxVal - editDef->minVal)); value += editDef->minVal; DC->setCVar(item->cvar, va("%f", value)); return qtrue; } } } } DC->Print("slider handle key exit\n"); return qfalse; } qboolean Item_HandleKey(itemDef_t *item, int key, qboolean down) { if (itemCapture) { Item_StopCapture(itemCapture); itemCapture = NULL; captureFunc = 0; captureData = NULL; } else { // bk001206 - parentheses if ( down && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) ) { Item_StartCapture(item, key); } } if (!down) { return qfalse; } switch (item->type) { case ITEM_TYPE_BUTTON: return qfalse; break; case ITEM_TYPE_RADIOBUTTON: return qfalse; break; case ITEM_TYPE_CHECKBOX: return qfalse; break; case ITEM_TYPE_EDITFIELD: case ITEM_TYPE_NUMERICFIELD: //return Item_TextField_HandleKey(item, key); return qfalse; break; case ITEM_TYPE_COMBO: return qfalse; break; case ITEM_TYPE_LISTBOX: return Item_ListBox_HandleKey(item, key, down, qfalse); break; case ITEM_TYPE_YESNO: return Item_YesNo_HandleKey(item, key); break; case ITEM_TYPE_MULTI: return Item_Multi_HandleKey(item, key); break; case ITEM_TYPE_OWNERDRAW: return Item_OwnerDraw_HandleKey(item, key); break; case ITEM_TYPE_BIND: return Item_Bind_HandleKey(item, key, down); break; case ITEM_TYPE_SLIDER: return Item_Slider_HandleKey(item, key, down); break; //case ITEM_TYPE_IMAGE: // Item_Image_Paint(item); // break; default: return qfalse; break; } //return qfalse; } void Item_Action(itemDef_t *item) { if (item) { Item_RunScript(item, item->action); } } itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu) { qboolean wrapped = qfalse; int oldCursor = menu->cursorItem; if (menu->cursorItem < 0) { menu->cursorItem = menu->itemCount-1; wrapped = qtrue; } while (menu->cursorItem > -1) { menu->cursorItem--; if (menu->cursorItem < 0 && !wrapped) { wrapped = qtrue; menu->cursorItem = menu->itemCount -1; } if (Item_SetFocus(menu->items[menu->cursorItem], DC->cursorx, DC->cursory)) { Menu_HandleMouseMove(menu, menu->items[menu->cursorItem]->window.rect.x + 1, menu->items[menu->cursorItem]->window.rect.y + 1); return menu->items[menu->cursorItem]; } } menu->cursorItem = oldCursor; return NULL; } itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu) { qboolean wrapped = qfalse; int oldCursor = menu->cursorItem; if (menu->cursorItem == -1) { menu->cursorItem = 0; wrapped = qtrue; } while (menu->cursorItem < menu->itemCount) { menu->cursorItem++; if (menu->cursorItem >= menu->itemCount && !wrapped) { wrapped = qtrue; menu->cursorItem = 0; } if (Item_SetFocus(menu->items[menu->cursorItem], DC->cursorx, DC->cursory)) { Menu_HandleMouseMove(menu, menu->items[menu->cursorItem]->window.rect.x + 1, menu->items[menu->cursorItem]->window.rect.y + 1); return menu->items[menu->cursorItem]; } } menu->cursorItem = oldCursor; return NULL; } static void Window_CloseCinematic(windowDef_t *window) { if (window->style == WINDOW_STYLE_CINEMATIC && window->cinematic >= 0) { DC->stopCinematic(window->cinematic); window->cinematic = -1; } } static void Menu_CloseCinematics(menuDef_t *menu) { if (menu) { int i; Window_CloseCinematic(&menu->window); for (i = 0; i < menu->itemCount; i++) { Window_CloseCinematic(&menu->items[i]->window); if (menu->items[i]->type == ITEM_TYPE_OWNERDRAW) { DC->stopCinematic(0-menu->items[i]->window.ownerDraw); } } } } static void Display_CloseCinematics( void ) { int i; for (i = 0; i < menuCount; i++) { Menu_CloseCinematics(&Menus[i]); } } void Menus_Activate(menuDef_t *menu) { menu->window.flags |= (WINDOW_HASFOCUS | WINDOW_VISIBLE); if (menu->onOpen) { itemDef_t item; item.parent = menu; Item_RunScript(&item, menu->onOpen); } if (menu->soundName && *menu->soundName) { // DC->stopBackgroundTrack(); // you don't want to do this since it will reset s_rawend DC->startBackgroundTrack(menu->soundName, menu->soundName); } Display_CloseCinematics(); } int Display_VisibleMenuCount( void ) { int i, count; count = 0; for (i = 0; i < menuCount; i++) { if (Menus[i].window.flags & (WINDOW_FORCED | WINDOW_VISIBLE)) { count++; } } return count; } void Menus_HandleOOBClick(menuDef_t *menu, int key, qboolean down) { if (menu) { int i; // basically the behaviour we are looking for is if there are windows in the stack.. see if // the cursor is within any of them.. if not close them otherwise activate them and pass the // key on.. force a mouse move to activate focus and script stuff if (down && menu->window.flags & WINDOW_OOB_CLICK) { Menu_RunCloseScript(menu); menu->window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); } for (i = 0; i < menuCount; i++) { if (Menu_OverActiveItem(&Menus[i], DC->cursorx, DC->cursory)) { Menu_RunCloseScript(menu); menu->window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); Menus_Activate(&Menus[i]); Menu_HandleMouseMove(&Menus[i], DC->cursorx, DC->cursory); Menu_HandleKey(&Menus[i], key, down); } } if (Display_VisibleMenuCount() == 0) { if (DC->Pause) { DC->Pause(qfalse); } } Display_CloseCinematics(); } } static rectDef_t *Item_CorrectedTextRect(itemDef_t *item) { static rectDef_t rect; memset(&rect, 0, sizeof(rectDef_t)); if (item) { rect = item->textRect; if (rect.w) { rect.y -= rect.h; } } return ▭ } void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) { int i; itemDef_t *item = NULL; qboolean inHandler = qfalse; if (inHandler) { return; } inHandler = qtrue; if (g_waitingForKey && down) { Item_Bind_HandleKey(g_bindItem, key, down); inHandler = qfalse; return; } if (g_editingField && down) { if (!Item_TextField_HandleKey(g_editItem, key)) { g_editingField = qfalse; g_editItem = NULL; inHandler = qfalse; return; } else if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3) { g_editingField = qfalse; g_editItem = NULL; Display_MouseMove(NULL, DC->cursorx, DC->cursory); } else if (key == K_TAB || key == K_UPARROW || key == K_DOWNARROW) { return; } } if (menu == NULL) { inHandler = qfalse; return; } // see if the mouse is within the window bounds and if so is this a mouse click if (down && !(menu->window.flags & WINDOW_POPUP) && !Rect_ContainsPoint(&menu->window.rect, DC->cursorx, DC->cursory)) { static qboolean inHandleKey = qfalse; // bk001206 - parentheses if (!inHandleKey && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) ) { inHandleKey = qtrue; Menus_HandleOOBClick(menu, key, down); inHandleKey = qfalse; inHandler = qfalse; return; } } // get the item with focus for (i = 0; i < menu->itemCount; i++) { if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { item = menu->items[i]; } } if (item != NULL) { if (Item_HandleKey(item, key, down)) { Item_Action(item); inHandler = qfalse; return; } } if (!down) { inHandler = qfalse; return; } // default handling switch ( key ) { case K_F11: if (DC->getCVarValue("developer")) { debugMode ^= 1; } break; case K_F12: if (DC->getCVarValue("developer")) { DC->executeText(EXEC_APPEND, "screenshot\n"); } break; case K_UPARROW: Menu_SetPrevCursorItem(menu); break; case K_ESCAPE: if (!g_waitingForKey && menu->onESC) { itemDef_t it; it.parent = menu; Item_RunScript(&it, menu->onESC); } break; case K_TAB: case K_DOWNARROW: Menu_SetNextCursorItem(menu); break; case K_MOUSE1: case K_MOUSE2: if (item) { if (item->type == ITEM_TYPE_TEXT) { if (Rect_ContainsPoint(Item_CorrectedTextRect(item), DC->cursorx, DC->cursory)) { Item_Action(item); } } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { item->cursorPos = 0; g_editingField = qtrue; g_editItem = item; } } else { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { Item_Action(item); } } } break; case K_JOY1: case K_JOY2: case K_JOY3: case K_JOY4: case K_AUX1: case K_AUX2: case K_AUX3: case K_AUX4: case K_AUX5: case K_AUX6: case K_AUX7: case K_AUX8: case K_AUX9: case K_AUX10: case K_AUX11: case K_AUX12: case K_AUX13: case K_AUX14: case K_AUX15: case K_AUX16: break; case K_KP_ENTER: case K_ENTER: if (item) { if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { item->cursorPos = 0; g_editingField = qtrue; g_editItem = item; } else { Item_Action(item); } } break; } inHandler = qfalse; } void ToWindowCoords(float *x, float *y, windowDef_t *window) { if (window->border != 0) { *x += window->borderSize; *y += window->borderSize; } *x += window->rect.x; *y += window->rect.y; } void Rect_ToWindowCoords(rectDef_t *rect, windowDef_t *window) { ToWindowCoords(&rect->x, &rect->y, window); } void Item_SetTextExtents(itemDef_t *item, int *width, int *height, const char *text) { const char *textPtr = (text) ? text : item->text; if (textPtr == NULL ) { return; } *width = item->textRect.w; *height = item->textRect.h; // keeps us from computing the widths and heights more than once if (*width == 0 || (item->type == ITEM_TYPE_OWNERDRAW && item->textalignment == ITEM_ALIGN_CENTER)) { int originalWidth = DC->textWidth(item->text, item->textscale, 0); if (item->type == ITEM_TYPE_OWNERDRAW && (item->textalignment == ITEM_ALIGN_CENTER || item->textalignment == ITEM_ALIGN_RIGHT)) { originalWidth += DC->ownerDrawWidth(item->window.ownerDraw, item->textscale); } else if (item->type == ITEM_TYPE_EDITFIELD && item->textalignment == ITEM_ALIGN_CENTER && item->cvar) { char buff[256]; DC->getCVarString(item->cvar, buff, 256); originalWidth += DC->textWidth(buff, item->textscale, 0); } *width = DC->textWidth(textPtr, item->textscale, 0); *height = DC->textHeight(textPtr, item->textscale, 0); item->textRect.w = *width; item->textRect.h = *height; item->textRect.x = item->textalignx; item->textRect.y = item->textaligny; if (item->textalignment == ITEM_ALIGN_RIGHT) { item->textRect.x = item->textalignx - originalWidth; } else if (item->textalignment == ITEM_ALIGN_CENTER) { item->textRect.x = item->textalignx - originalWidth / 2; } ToWindowCoords(&item->textRect.x, &item->textRect.y, &item->window); } } void Item_TextColor(itemDef_t *item, vec4_t *newColor) { vec4_t lowLight; menuDef_t *parent = (menuDef_t*)item->parent; Fade(&item->window.flags, &item->window.foreColor[3], parent->fadeClamp, &item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount); if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,*newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else if (item->textStyle == ITEM_TEXTSTYLE_BLINK && !((DC->realTime/BLINK_DIVISOR) & 1)) { lowLight[0] = 0.8 * item->window.foreColor[0]; lowLight[1] = 0.8 * item->window.foreColor[1]; lowLight[2] = 0.8 * item->window.foreColor[2]; lowLight[3] = 0.8 * item->window.foreColor[3]; LerpColor(item->window.foreColor,lowLight,*newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(newColor, &item->window.foreColor, sizeof(vec4_t)); // items can be enabled and disabled based on cvars } if (item->enableCvar && *item->enableCvar && item->cvarTest && *item->cvarTest) { if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { memcpy(newColor, &parent->disableColor, sizeof(vec4_t)); } } } void Item_Text_AutoWrapped_Paint(itemDef_t *item) { char text[1024]; const char *p, *textPtr, *newLinePtr; char buff[1024]; int width, height, len, textWidth, newLine, newLineWidth; float y; vec4_t color; textWidth = 0; newLinePtr = NULL; if (item->text == NULL) { if (item->cvar == NULL) { return; } else { DC->getCVarString(item->cvar, text, sizeof(text)); textPtr = text; } } else { textPtr = item->text; } if (*textPtr == '\0') { return; } Item_TextColor(item, &color); Item_SetTextExtents(item, &width, &height, textPtr); y = item->textaligny; len = 0; buff[0] = '\0'; newLine = 0; newLineWidth = 0; p = textPtr; while (p) { if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\0') { newLine = len; newLinePtr = p+1; newLineWidth = textWidth; } textWidth = DC->textWidth(buff, item->textscale, 0); if ( (newLine && textWidth > item->window.rect.w) || *p == '\n' || *p == '\0') { if (len) { if (item->textalignment == ITEM_ALIGN_LEFT) { item->textRect.x = item->textalignx; } else if (item->textalignment == ITEM_ALIGN_RIGHT) { item->textRect.x = item->textalignx - newLineWidth; } else if (item->textalignment == ITEM_ALIGN_CENTER) { item->textRect.x = item->textalignx - newLineWidth / 2; } item->textRect.y = y; ToWindowCoords(&item->textRect.x, &item->textRect.y, &item->window); // buff[newLine] = '\0'; DC->drawText(item->textRect.x, item->textRect.y, item->textscale, color, buff, 0, 0, item->textStyle); } if (*p == '\0') { break; } // y += height + 5; p = newLinePtr; len = 0; newLine = 0; newLineWidth = 0; continue; } buff[len++] = *p++; buff[len] = '\0'; } } void Item_Text_Wrapped_Paint(itemDef_t *item) { char text[1024]; const char *p, *start, *textPtr; char buff[1024]; int width, height; float x, y; vec4_t color; // now paint the text and/or any optional images // default to left if (item->text == NULL) { if (item->cvar == NULL) { return; } else { DC->getCVarString(item->cvar, text, sizeof(text)); textPtr = text; } } else { textPtr = item->text; } if (*textPtr == '\0') { return; } Item_TextColor(item, &color); Item_SetTextExtents(item, &width, &height, textPtr); x = item->textRect.x; y = item->textRect.y; start = textPtr; p = strchr(textPtr, '\r'); while (p && *p) { strncpy(buff, start, p-start+1); buff[p-start] = '\0'; DC->drawText(x, y, item->textscale, color, buff, 0, 0, item->textStyle); y += height + 5; start += p - start + 1; p = strchr(p+1, '\r'); } DC->drawText(x, y, item->textscale, color, start, 0, 0, item->textStyle); } void Item_Text_Paint(itemDef_t *item) { char text[1024]; const char *textPtr; int height, width; vec4_t color; if (item->window.flags & WINDOW_WRAPPED) { Item_Text_Wrapped_Paint(item); return; } if (item->window.flags & WINDOW_AUTOWRAPPED) { Item_Text_AutoWrapped_Paint(item); return; } if (item->text == NULL) { if (item->cvar == NULL) { return; } else { DC->getCVarString(item->cvar, text, sizeof(text)); textPtr = text; } } else { textPtr = item->text; } // this needs to go here as it sets extents for cvar types as well Item_SetTextExtents(item, &width, &height, textPtr); if (*textPtr == '\0') { return; } Item_TextColor(item, &color); //FIXME: this is a fucking mess /* adjust = 0; if (item->textStyle == ITEM_TEXTSTYLE_OUTLINED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { adjust = 0.5; } if (item->textStyle == ITEM_TEXTSTYLE_SHADOWED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { Fade(&item->window.flags, &DC->Assets.shadowColor[3], DC->Assets.fadeClamp, &item->window.nextTime, DC->Assets.fadeCycle, qfalse); DC->drawText(item->textRect.x + DC->Assets.shadowX, item->textRect.y + DC->Assets.shadowY, item->textscale, DC->Assets.shadowColor, textPtr, adjust); } */ // if (item->textStyle == ITEM_TEXTSTYLE_OUTLINED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { // Fade(&item->window.flags, &item->window.outlineColor[3], DC->Assets.fadeClamp, &item->window.nextTime, DC->Assets.fadeCycle, qfalse); // /* // Text_Paint(item->textRect.x-1, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x+1, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x-1, item->textRect.y, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x+1, item->textRect.y, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x-1, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); // Text_Paint(item->textRect.x+1, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); // */ // DC->drawText(item->textRect.x - 1, item->textRect.y + 1, item->textscale * 1.02, item->window.outlineColor, textPtr, adjust); // } DC->drawText(item->textRect.x, item->textRect.y, item->textscale, color, textPtr, 0, 0, item->textStyle); } //float trap_Cvar_VariableValue( const char *var_name ); //void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void Item_TextField_Paint(itemDef_t *item) { char buff[1024]; vec4_t newColor, lowLight; int offset; menuDef_t *parent = (menuDef_t*)item->parent; editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; Item_Text_Paint(item); buff[0] = '\0'; if (item->cvar) { DC->getCVarString(item->cvar, buff, sizeof(buff)); } parent = (menuDef_t*)item->parent; if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } offset = (item->text && *item->text) ? 8 : 0; if (item->window.flags & WINDOW_HASFOCUS && g_editingField) { char cursor = DC->getOverstrikeMode() ? '_' : '|'; DC->drawTextWithCursor(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, item->cursorPos - editPtr->paintOffset , cursor, editPtr->maxPaintChars, item->textStyle); } else { DC->drawText(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, 0, editPtr->maxPaintChars, item->textStyle); } } void Item_YesNo_Paint(itemDef_t *item) { vec4_t newColor, lowLight; float value; menuDef_t *parent = (menuDef_t*)item->parent; value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } if (item->text) { Item_Text_Paint(item); DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); } } void Item_Multi_Paint(itemDef_t *item) { vec4_t newColor, lowLight; const char *text = ""; menuDef_t *parent = (menuDef_t*)item->parent; if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } text = Item_Multi_Setting(item); if (item->text) { Item_Text_Paint(item); DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); } } typedef struct { char *command; int id; int defaultbind1; int defaultbind2; int bind1; int bind2; } bind_t; typedef struct { char* name; float defaultvalue; float value; } configcvar_t; static bind_t g_bindings[] = { {"+scores", K_TAB, -1, -1, -1}, {"+button2", K_ENTER, -1, -1, -1}, {"+speed", K_SHIFT, -1, -1, -1}, {"+forward", K_UPARROW, -1, -1, -1}, {"+back", K_DOWNARROW, -1, -1, -1}, {"+moveleft", ',', -1, -1, -1}, {"+moveright", '.', -1, -1, -1}, {"+moveup", K_SPACE, -1, -1, -1}, {"+movedown", 'c', -1, -1, -1}, {"+left", K_LEFTARROW, -1, -1, -1}, {"+right", K_RIGHTARROW, -1, -1, -1}, {"+strafe", K_ALT, -1, -1, -1}, {"+lookup", K_PGDN, -1, -1, -1}, {"+lookdown", K_DEL, -1, -1, -1}, {"+mlook", '/', -1, -1, -1}, {"centerview", K_END, -1, -1, -1}, {"+zoom", -1, -1, -1, -1}, {"weapon 1", '1', -1, -1, -1}, {"weapon 2", '2', -1, -1, -1}, {"weapon 3", '3', -1, -1, -1}, {"weapon 4", '4', -1, -1, -1}, {"weapon 5", '5', -1, -1, -1}, {"weapon 6", '6', -1, -1, -1}, {"weapon 7", '7', -1, -1, -1}, {"weapon 8", '8', -1, -1, -1}, {"weapon 9", '9', -1, -1, -1}, {"weapon 10", '0', -1, -1, -1}, {"weapon 11", -1, -1, -1, -1}, {"weapon 12", -1, -1, -1, -1}, {"weapon 13", -1, -1, -1, -1}, {"+attack", K_CTRL, -1, -1, -1}, {"weapprev", '[', -1, -1, -1}, {"weapnext", ']', -1, -1, -1}, {"+button3", K_MOUSE3, -1, -1, -1}, {"+button4", K_MOUSE4, -1, -1, -1}, {"prevTeamMember", 'w', -1, -1, -1}, {"nextTeamMember", 'r', -1, -1, -1}, {"nextOrder", 't', -1, -1, -1}, {"confirmOrder", 'y', -1, -1, -1}, {"denyOrder", 'n', -1, -1, -1}, {"taskOffense", 'o', -1, -1, -1}, {"taskDefense", 'd', -1, -1, -1}, {"taskPatrol", 'p', -1, -1, -1}, {"taskCamp", 'c', -1, -1, -1}, {"taskFollow", 'f', -1, -1, -1}, {"taskRetrieve", 'v', -1, -1, -1}, {"taskEscort", 'e', -1, -1, -1}, {"taskOwnFlag", 'i', -1, -1, -1}, {"taskSuicide", 'k', -1, -1, -1}, {"tauntKillInsult", K_F1, -1, -1, -1}, {"tauntPraise", K_F2, -1, -1, -1}, {"tauntTaunt", K_F3, -1, -1, -1}, {"tauntDeathInsult", K_F4, -1, -1, -1}, {"tauntGauntlet", K_F5, -1, -1, -1}, {"scoresUp", K_KP_PGUP, -1, -1, -1}, {"scoresDown", K_KP_PGDN, -1, -1, -1}, // bk001205 - this one below was: '-1' {"messagemode", -1, -1, -1, -1}, {"messagemode2", -1, -1, -1, -1}, {"messagemode3", -1, -1, -1, -1}, {"messagemode4", -1, -1, -1, -1} }; static const int g_bindCount = sizeof(g_bindings) / sizeof(bind_t); #ifndef MISSIONPACK // bk001206 static configcvar_t g_configcvars[] = { {"cl_run", 0, 0}, {"m_pitch", 0, 0}, {"cg_autoswitch", 0, 0}, {"sensitivity", 0, 0}, {"in_joystick", 0, 0}, {"joy_threshold", 0, 0}, {"m_filter", 0, 0}, {"cl_freelook", 0, 0}, {NULL, 0, 0} }; #endif /* ================= Controls_GetKeyAssignment ================= */ static void Controls_GetKeyAssignment (char *command, int *twokeys) { int count; int j; char b[256]; twokeys[0] = twokeys[1] = -1; count = 0; for ( j = 0; j < 256; j++ ) { DC->getBindingBuf( j, b, 256 ); if ( *b == 0 ) { continue; } if ( !Q_stricmp( b, command ) ) { twokeys[count] = j; count++; if (count == 2) { break; } } } } /* ================= Controls_GetConfig ================= */ void Controls_GetConfig( void ) { int i; int twokeys[2]; // iterate each command, get its numeric binding for (i=0; i < g_bindCount; i++) { Controls_GetKeyAssignment(g_bindings[i].command, twokeys); g_bindings[i].bind1 = twokeys[0]; g_bindings[i].bind2 = twokeys[1]; } //s_controls.invertmouse.curvalue = DC->getCVarValue( "m_pitch" ) < 0; //s_controls.smoothmouse.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "m_filter" ) ); //s_controls.alwaysrun.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_run" ) ); //s_controls.autoswitch.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cg_autoswitch" ) ); //s_controls.sensitivity.curvalue = UI_ClampCvar( 2, 30, Controls_GetCvarValue( "sensitivity" ) ); //s_controls.joyenable.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "in_joystick" ) ); //s_controls.joythreshold.curvalue = UI_ClampCvar( 0.05, 0.75, Controls_GetCvarValue( "joy_threshold" ) ); //s_controls.freelook.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) ); } /* ================= Controls_SetConfig ================= */ void Controls_SetConfig(qboolean restart) { int i; // iterate each command, get its numeric binding for (i=0; i < g_bindCount; i++) { if (g_bindings[i].bind1 != -1) { DC->setBinding( g_bindings[i].bind1, g_bindings[i].command ); if (g_bindings[i].bind2 != -1) DC->setBinding( g_bindings[i].bind2, g_bindings[i].command ); } } //if ( s_controls.invertmouse.curvalue ) // DC->setCVar("m_pitch", va("%f),-fabs( DC->getCVarValue( "m_pitch" ) ) ); //else // trap_Cvar_SetValue( "m_pitch", fabs( trap_Cvar_VariableValue( "m_pitch" ) ) ); //trap_Cvar_SetValue( "m_filter", s_controls.smoothmouse.curvalue ); //trap_Cvar_SetValue( "cl_run", s_controls.alwaysrun.curvalue ); //trap_Cvar_SetValue( "cg_autoswitch", s_controls.autoswitch.curvalue ); //trap_Cvar_SetValue( "sensitivity", s_controls.sensitivity.curvalue ); //trap_Cvar_SetValue( "in_joystick", s_controls.joyenable.curvalue ); //trap_Cvar_SetValue( "joy_threshold", s_controls.joythreshold.curvalue ); //trap_Cvar_SetValue( "cl_freelook", s_controls.freelook.curvalue ); DC->executeText(EXEC_APPEND, "in_restart\n"); //trap_Cmd_ExecuteText( EXEC_APPEND, "in_restart\n" ); } /* ================= Controls_SetDefaults ================= */ void Controls_SetDefaults( void ) { int i; // iterate each command, set its default binding for (i=0; i < g_bindCount; i++) { g_bindings[i].bind1 = g_bindings[i].defaultbind1; g_bindings[i].bind2 = g_bindings[i].defaultbind2; } //s_controls.invertmouse.curvalue = Controls_GetCvarDefault( "m_pitch" ) < 0; //s_controls.smoothmouse.curvalue = Controls_GetCvarDefault( "m_filter" ); //s_controls.alwaysrun.curvalue = Controls_GetCvarDefault( "cl_run" ); //s_controls.autoswitch.curvalue = Controls_GetCvarDefault( "cg_autoswitch" ); //s_controls.sensitivity.curvalue = Controls_GetCvarDefault( "sensitivity" ); //s_controls.joyenable.curvalue = Controls_GetCvarDefault( "in_joystick" ); //s_controls.joythreshold.curvalue = Controls_GetCvarDefault( "joy_threshold" ); //s_controls.freelook.curvalue = Controls_GetCvarDefault( "cl_freelook" ); } int BindingIDFromName(const char *name) { int i; for (i=0; i < g_bindCount; i++) { if (Q_stricmp(name, g_bindings[i].command) == 0) { return i; } } return -1; } char g_nameBind1[32]; char g_nameBind2[32]; void BindingFromName(const char *cvar) { int i, b1, b2; // iterate each command, set its default binding for (i=0; i < g_bindCount; i++) { if (Q_stricmp(cvar, g_bindings[i].command) == 0) { b1 = g_bindings[i].bind1; if (b1 == -1) { break; } DC->keynumToStringBuf( b1, g_nameBind1, 32 ); Q_strupr(g_nameBind1); b2 = g_bindings[i].bind2; if (b2 != -1) { DC->keynumToStringBuf( b2, g_nameBind2, 32 ); Q_strupr(g_nameBind2); strcat( g_nameBind1, " or " ); strcat( g_nameBind1, g_nameBind2 ); } return; } } strcpy(g_nameBind1, "???"); } void Item_Slider_Paint(itemDef_t *item) { vec4_t newColor, lowLight; float x, y, value; menuDef_t *parent = (menuDef_t*)item->parent; value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } y = item->window.rect.y; if (item->text) { Item_Text_Paint(item); x = item->textRect.x + item->textRect.w + 8; } else { x = item->window.rect.x; } DC->setColor(newColor); DC->drawHandlePic( x, y, SLIDER_WIDTH, SLIDER_HEIGHT, DC->Assets.sliderBar ); x = Item_Slider_ThumbPosition(item); DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y - 2, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); } void Item_Bind_Paint(itemDef_t *item) { vec4_t newColor, lowLight; float value; int maxChars = 0; menuDef_t *parent = (menuDef_t*)item->parent; editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; if (editPtr) { maxChars = editPtr->maxPaintChars; } value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; if (item->window.flags & WINDOW_HASFOCUS) { if (g_bindItem == item) { lowLight[0] = 0.8f * 1.0f; lowLight[1] = 0.8f * 0.0f; lowLight[2] = 0.8f * 0.0f; lowLight[3] = 0.8f * 1.0f; } else { lowLight[0] = 0.8f * parent->focusColor[0]; lowLight[1] = 0.8f * parent->focusColor[1]; lowLight[2] = 0.8f * parent->focusColor[2]; lowLight[3] = 0.8f * parent->focusColor[3]; } LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } if (item->text) { Item_Text_Paint(item); BindingFromName(item->cvar); DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "FIXME" : "FIXME", 0, maxChars, item->textStyle); } } qboolean Display_KeyBindPending(void) { return g_waitingForKey; } qboolean Item_Bind_HandleKey(itemDef_t *item, int key, qboolean down) { int id; int i; if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && !g_waitingForKey) { if (down && (key == K_MOUSE1 || key == K_ENTER)) { g_waitingForKey = qtrue; g_bindItem = item; } return qtrue; } else { if (!g_waitingForKey || g_bindItem == NULL) { return qtrue; } if (key & K_CHAR_FLAG) { return qtrue; } switch (key) { case K_ESCAPE: g_waitingForKey = qfalse; return qtrue; case K_BACKSPACE: id = BindingIDFromName(item->cvar); if (id != -1) { g_bindings[id].bind1 = -1; g_bindings[id].bind2 = -1; } Controls_SetConfig(qtrue); g_waitingForKey = qfalse; g_bindItem = NULL; return qtrue; case '`': return qtrue; } } if (key != -1) { for (i=0; i < g_bindCount; i++) { if (g_bindings[i].bind2 == key) { g_bindings[i].bind2 = -1; } if (g_bindings[i].bind1 == key) { g_bindings[i].bind1 = g_bindings[i].bind2; g_bindings[i].bind2 = -1; } } } id = BindingIDFromName(item->cvar); if (id != -1) { if (key == -1) { if( g_bindings[id].bind1 != -1 ) { DC->setBinding( g_bindings[id].bind1, "" ); g_bindings[id].bind1 = -1; } if( g_bindings[id].bind2 != -1 ) { DC->setBinding( g_bindings[id].bind2, "" ); g_bindings[id].bind2 = -1; } } else if (g_bindings[id].bind1 == -1) { g_bindings[id].bind1 = key; } else if (g_bindings[id].bind1 != key && g_bindings[id].bind2 == -1) { g_bindings[id].bind2 = key; } else { DC->setBinding( g_bindings[id].bind1, "" ); DC->setBinding( g_bindings[id].bind2, "" ); g_bindings[id].bind1 = key; g_bindings[id].bind2 = -1; } } Controls_SetConfig(qtrue); g_waitingForKey = qfalse; return qtrue; } void AdjustFrom640(float *x, float *y, float *w, float *h) { //*x = *x * DC->scale + DC->bias; *x *= DC->xscale; *y *= DC->yscale; *w *= DC->xscale; *h *= DC->yscale; } void Item_Model_Paint(itemDef_t *item) { float x, y, w, h; refdef_t refdef; refEntity_t ent; vec3_t mins, maxs, origin; vec3_t angles; modelDef_t *modelPtr = (modelDef_t*)item->typeData; if (modelPtr == NULL) { return; } // setup the refdef memset( &refdef, 0, sizeof( refdef ) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); x = item->window.rect.x+1; y = item->window.rect.y+1; w = item->window.rect.w-2; h = item->window.rect.h-2; AdjustFrom640( &x, &y, &w, &h ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; DC->modelBounds( item->asset, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the model nearly fills the box if (qtrue) { float len = 0.5 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) //origin[0] = len / tan(w/2); } else { origin[0] = item->textscale; } refdef.fov_x = (modelPtr->fov_x) ? modelPtr->fov_x : w; refdef.fov_y = (modelPtr->fov_y) ? modelPtr->fov_y : h; //refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); //xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); //refdef.fov_y = atan2( refdef.height, xx ); //refdef.fov_y *= ( 360 / M_PI ); DC->clearScene(); refdef.time = DC->realTime; // add the model memset( &ent, 0, sizeof(ent) ); //adjust = 5.0 * sin( (float)uis.realtime / 500 ); //adjust = 360 % (int)((float)uis.realtime / 1000); //VectorSet( angles, 0, 0, 1 ); // use item storage to track if (modelPtr->rotationSpeed) { if (DC->realTime > item->window.nextTime) { item->window.nextTime = DC->realTime + modelPtr->rotationSpeed; modelPtr->angle = (int)(modelPtr->angle + 1) % 360; } } VectorSet( angles, 0, modelPtr->angle, 0 ); AnglesToAxis( angles, ent.axis ); ent.hModel = item->asset; VectorCopy( origin, ent.origin ); VectorCopy( origin, ent.lightingOrigin ); ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; VectorCopy( ent.origin, ent.oldorigin ); DC->addRefEntityToScene( &ent ); DC->renderScene( &refdef ); } void Item_Image_Paint(itemDef_t *item) { if (item == NULL) { return; } DC->drawHandlePic(item->window.rect.x+1, item->window.rect.y+1, item->window.rect.w-2, item->window.rect.h-2, item->asset); } void Item_ListBox_Paint(itemDef_t *item) { float x, y, size, thumb; int count, i; qhandle_t image; qhandle_t optionalImage; listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; // the listbox is horizontal or vertical and has a fixed size scroll bar going either direction // elements are enumerated from the DC and either text or image handles are acquired from the DC as well // textscale is used to size the text, textalignx and textaligny are used to size image elements // there is no clipping available so only the last completely visible item is painted count = DC->feederCount(item->special); // default is vertical if horizontal flag is not here if (item->window.flags & WINDOW_HORIZONTAL) { // draw scrollbar in bottom of the window // bar x = item->window.rect.x + 1; y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE - 1; DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowLeft); x += SCROLLBAR_SIZE - 1; size = item->window.rect.w - (SCROLLBAR_SIZE * 2); DC->drawHandlePic(x, y, size+1, SCROLLBAR_SIZE, DC->Assets.scrollBar); x += size - 1; DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowRight); // thumb thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); if (thumb > x - SCROLLBAR_SIZE - 1) { thumb = x - SCROLLBAR_SIZE - 1; } DC->drawHandlePic(thumb, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); // listPtr->endPos = listPtr->startPos; size = item->window.rect.w - 2; // items // size contains max available space if (listPtr->elementStyle == LISTBOX_IMAGE) { // fit = 0; x = item->window.rect.x + 1; y = item->window.rect.y + 1; for (i = listPtr->startPos; i < count; i++) { // always draw at least one // which may overdraw the box if it is too small for the element image = DC->feederItemImage(item->special, i); if (image) { DC->drawHandlePic(x+1, y+1, listPtr->elementWidth - 2, listPtr->elementHeight - 2, image); } if (i == item->cursorPos) { DC->drawRect(x, y, listPtr->elementWidth-1, listPtr->elementHeight-1, item->window.borderSize, item->window.borderColor); } size -= listPtr->elementWidth; if (size < listPtr->elementWidth) { listPtr->drawPadding = size; //listPtr->elementWidth - size; break; } x += listPtr->elementWidth; listPtr->endPos++; // fit++; } } else { // } } else { // draw scrollbar to right side of the window x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE - 1; y = item->window.rect.y + 1; DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowUp); y += SCROLLBAR_SIZE - 1; listPtr->endPos = listPtr->startPos; size = item->window.rect.h - (SCROLLBAR_SIZE * 2); DC->drawHandlePic(x, y, SCROLLBAR_SIZE, size+1, DC->Assets.scrollBar); y += size - 1; DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowDown); // thumb thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); if (thumb > y - SCROLLBAR_SIZE - 1) { thumb = y - SCROLLBAR_SIZE - 1; } DC->drawHandlePic(x, thumb, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); // adjust size for item painting size = item->window.rect.h - 2; if (listPtr->elementStyle == LISTBOX_IMAGE) { // fit = 0; x = item->window.rect.x + 1; y = item->window.rect.y + 1; for (i = listPtr->startPos; i < count; i++) { // always draw at least one // which may overdraw the box if it is too small for the element image = DC->feederItemImage(item->special, i); if (image) { DC->drawHandlePic(x+1, y+1, listPtr->elementWidth - 2, listPtr->elementHeight - 2, image); } if (i == item->cursorPos) { DC->drawRect(x, y, listPtr->elementWidth - 1, listPtr->elementHeight - 1, item->window.borderSize, item->window.borderColor); } listPtr->endPos++; size -= listPtr->elementWidth; if (size < listPtr->elementHeight) { listPtr->drawPadding = listPtr->elementHeight - size; break; } y += listPtr->elementHeight; // fit++; } } else { x = item->window.rect.x + 1; y = item->window.rect.y + 1; for (i = listPtr->startPos; i < count; i++) { const char *text; // always draw at least one // which may overdraw the box if it is too small for the element if (listPtr->numColumns > 0) { int j; for (j = 0; j < listPtr->numColumns; j++) { text = DC->feederItemText(item->special, i, j, &optionalImage); if (optionalImage >= 0) { DC->drawHandlePic(x + 4 + listPtr->columnInfo[j].pos, y - 1 + listPtr->elementHeight / 2, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); } else if (text) { DC->drawText(x + 4 + listPtr->columnInfo[j].pos, y + listPtr->elementHeight, item->textscale, item->window.foreColor, text, 0, listPtr->columnInfo[j].maxChars, item->textStyle); } } } else { text = DC->feederItemText(item->special, i, 0, &optionalImage); if (optionalImage >= 0) { //DC->drawHandlePic(x + 4 + listPtr->elementHeight, y, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); } else if (text) { DC->drawText(x + 4, y + listPtr->elementHeight, item->textscale, item->window.foreColor, text, 0, 0, item->textStyle); } } if (i == item->cursorPos) { DC->fillRect(x + 2, y + 2, item->window.rect.w - SCROLLBAR_SIZE - 4, listPtr->elementHeight, item->window.outlineColor); } size -= listPtr->elementHeight; if (size < listPtr->elementHeight) { listPtr->drawPadding = listPtr->elementHeight - size; break; } listPtr->endPos++; y += listPtr->elementHeight; // fit++; } } } } void Item_OwnerDraw_Paint(itemDef_t *item) { menuDef_t *parent; if (item == NULL) { return; } parent = (menuDef_t*)item->parent; if (DC->ownerDrawItem) { vec4_t color, lowLight; menuDef_t *parent = (menuDef_t*)item->parent; Fade(&item->window.flags, &item->window.foreColor[3], parent->fadeClamp, &item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount); memcpy(&color, &item->window.foreColor, sizeof(color)); if (item->numColors > 0 && DC->getValue) { // if the value is within one of the ranges then set color to that, otherwise leave at default int i; float f = DC->getValue(item->window.ownerDraw); for (i = 0; i < item->numColors; i++) { if (f >= item->colorRanges[i].low && f <= item->colorRanges[i].high) { memcpy(&color, &item->colorRanges[i].color, sizeof(color)); break; } } } if (item->window.flags & WINDOW_HASFOCUS) { lowLight[0] = 0.8 * parent->focusColor[0]; lowLight[1] = 0.8 * parent->focusColor[1]; lowLight[2] = 0.8 * parent->focusColor[2]; lowLight[3] = 0.8 * parent->focusColor[3]; LerpColor(parent->focusColor,lowLight,color,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } else if (item->textStyle == ITEM_TEXTSTYLE_BLINK && !((DC->realTime/BLINK_DIVISOR) & 1)) { lowLight[0] = 0.8 * item->window.foreColor[0]; lowLight[1] = 0.8 * item->window.foreColor[1]; lowLight[2] = 0.8 * item->window.foreColor[2]; lowLight[3] = 0.8 * item->window.foreColor[3]; LerpColor(item->window.foreColor,lowLight,color,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); } if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { Com_Memcpy(color, parent->disableColor, sizeof(vec4_t)); } if (item->text) { Item_Text_Paint(item); if (item->text[0]) { // +8 is an offset kludge to properly align owner draw items that have text combined with them DC->ownerDrawItem(item->textRect.x + item->textRect.w + 8, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); } else { DC->ownerDrawItem(item->textRect.x + item->textRect.w, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); } } else { DC->ownerDrawItem(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, item->textalignx, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); } } } void Item_Paint(itemDef_t *item) { vec4_t red; menuDef_t *parent = (menuDef_t*)item->parent; red[0] = red[3] = 1; red[1] = red[2] = 0; if (item == NULL) { return; } if (item->window.flags & WINDOW_ORBITING) { if (DC->realTime > item->window.nextTime) { float rx, ry, a, c, s, w, h; item->window.nextTime = DC->realTime + item->window.offsetTime; // translate w = item->window.rectClient.w / 2; h = item->window.rectClient.h / 2; rx = item->window.rectClient.x + w - item->window.rectEffects.x; ry = item->window.rectClient.y + h - item->window.rectEffects.y; a = 3 * M_PI / 180; c = cos(a); s = sin(a); item->window.rectClient.x = (rx * c - ry * s) + item->window.rectEffects.x - w; item->window.rectClient.y = (rx * s + ry * c) + item->window.rectEffects.y - h; Item_UpdatePosition(item); } } if (item->window.flags & WINDOW_INTRANSITION) { if (DC->realTime > item->window.nextTime) { int done = 0; item->window.nextTime = DC->realTime + item->window.offsetTime; // transition the x,y if (item->window.rectClient.x == item->window.rectEffects.x) { done++; } else { if (item->window.rectClient.x < item->window.rectEffects.x) { item->window.rectClient.x += item->window.rectEffects2.x; if (item->window.rectClient.x > item->window.rectEffects.x) { item->window.rectClient.x = item->window.rectEffects.x; done++; } } else { item->window.rectClient.x -= item->window.rectEffects2.x; if (item->window.rectClient.x < item->window.rectEffects.x) { item->window.rectClient.x = item->window.rectEffects.x; done++; } } } if (item->window.rectClient.y == item->window.rectEffects.y) { done++; } else { if (item->window.rectClient.y < item->window.rectEffects.y) { item->window.rectClient.y += item->window.rectEffects2.y; if (item->window.rectClient.y > item->window.rectEffects.y) { item->window.rectClient.y = item->window.rectEffects.y; done++; } } else { item->window.rectClient.y -= item->window.rectEffects2.y; if (item->window.rectClient.y < item->window.rectEffects.y) { item->window.rectClient.y = item->window.rectEffects.y; done++; } } } if (item->window.rectClient.w == item->window.rectEffects.w) { done++; } else { if (item->window.rectClient.w < item->window.rectEffects.w) { item->window.rectClient.w += item->window.rectEffects2.w; if (item->window.rectClient.w > item->window.rectEffects.w) { item->window.rectClient.w = item->window.rectEffects.w; done++; } } else { item->window.rectClient.w -= item->window.rectEffects2.w; if (item->window.rectClient.w < item->window.rectEffects.w) { item->window.rectClient.w = item->window.rectEffects.w; done++; } } } if (item->window.rectClient.h == item->window.rectEffects.h) { done++; } else { if (item->window.rectClient.h < item->window.rectEffects.h) { item->window.rectClient.h += item->window.rectEffects2.h; if (item->window.rectClient.h > item->window.rectEffects.h) { item->window.rectClient.h = item->window.rectEffects.h; done++; } } else { item->window.rectClient.h -= item->window.rectEffects2.h; if (item->window.rectClient.h < item->window.rectEffects.h) { item->window.rectClient.h = item->window.rectEffects.h; done++; } } } Item_UpdatePosition(item); if (done == 4) { item->window.flags &= ~WINDOW_INTRANSITION; } } } if (item->window.ownerDrawFlags && DC->ownerDrawVisible) { if (!DC->ownerDrawVisible(item->window.ownerDrawFlags)) { item->window.flags &= ~WINDOW_VISIBLE; } else { item->window.flags |= WINDOW_VISIBLE; } } if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE)) { if (!Item_EnableShowViaCvar(item, CVAR_SHOW)) { return; } } if (item->window.flags & WINDOW_TIMEDVISIBLE) { } if (!(item->window.flags & WINDOW_VISIBLE)) { return; } // paint the rect first.. Window_Paint(&item->window, parent->fadeAmount , parent->fadeClamp, parent->fadeCycle); if (debugMode) { vec4_t color; rectDef_t *r = Item_CorrectedTextRect(item); color[1] = color[3] = 1; color[0] = color[2] = 0; DC->drawRect(r->x, r->y, r->w, r->h, 1, color); } //DC->drawRect(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, 1, red); switch (item->type) { case ITEM_TYPE_OWNERDRAW: Item_OwnerDraw_Paint(item); break; case ITEM_TYPE_TEXT: case ITEM_TYPE_BUTTON: Item_Text_Paint(item); break; case ITEM_TYPE_RADIOBUTTON: break; case ITEM_TYPE_CHECKBOX: break; case ITEM_TYPE_EDITFIELD: case ITEM_TYPE_NUMERICFIELD: Item_TextField_Paint(item); break; case ITEM_TYPE_COMBO: break; case ITEM_TYPE_LISTBOX: Item_ListBox_Paint(item); break; //case ITEM_TYPE_IMAGE: // Item_Image_Paint(item); // break; case ITEM_TYPE_MODEL: Item_Model_Paint(item); break; case ITEM_TYPE_YESNO: Item_YesNo_Paint(item); break; case ITEM_TYPE_MULTI: Item_Multi_Paint(item); break; case ITEM_TYPE_BIND: Item_Bind_Paint(item); break; case ITEM_TYPE_SLIDER: Item_Slider_Paint(item); break; default: break; } } void Menu_Init(menuDef_t *menu) { memset(menu, 0, sizeof(menuDef_t)); menu->cursorItem = -1; menu->fadeAmount = DC->Assets.fadeAmount; menu->fadeClamp = DC->Assets.fadeClamp; menu->fadeCycle = DC->Assets.fadeCycle; Window_Init(&menu->window); } itemDef_t *Menu_GetFocusedItem(menuDef_t *menu) { int i; if (menu) { for (i = 0; i < menu->itemCount; i++) { if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { return menu->items[i]; } } } return NULL; } menuDef_t *Menu_GetFocused(void) { int i; for (i = 0; i < menuCount; i++) { if (Menus[i].window.flags & WINDOW_HASFOCUS && Menus[i].window.flags & WINDOW_VISIBLE) { return &Menus[i]; } } return NULL; } void Menu_ScrollFeeder(menuDef_t *menu, int feeder, qboolean down) { if (menu) { int i; for (i = 0; i < menu->itemCount; i++) { if (menu->items[i]->special == feeder) { Item_ListBox_HandleKey(menu->items[i], (down) ? K_DOWNARROW : K_UPARROW, qtrue, qtrue); return; } } } } void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name) { if (menu == NULL) { if (name == NULL) { menu = Menu_GetFocused(); } else { menu = Menus_FindByName(name); } } if (menu) { int i; for (i = 0; i < menu->itemCount; i++) { if (menu->items[i]->special == feeder) { if (index == 0) { listBoxDef_t *listPtr = (listBoxDef_t*)menu->items[i]->typeData; listPtr->cursorPos = 0; listPtr->startPos = 0; } menu->items[i]->cursorPos = index; DC->feederSelection(menu->items[i]->special, menu->items[i]->cursorPos); return; } } } } qboolean Menus_AnyFullScreenVisible(void) { int i; for (i = 0; i < menuCount; i++) { if (Menus[i].window.flags & WINDOW_VISIBLE && Menus[i].fullScreen) { return qtrue; } } return qfalse; } menuDef_t *Menus_ActivateByName(const char *p) { int i; menuDef_t *m = NULL; menuDef_t *focus = Menu_GetFocused(); for (i = 0; i < menuCount; i++) { if (Q_stricmp(Menus[i].window.name, p) == 0) { m = &Menus[i]; Menus_Activate(m); if (openMenuCount < MAX_OPEN_MENUS && focus != NULL) { menuStack[openMenuCount++] = focus; } } else { Menus[i].window.flags &= ~WINDOW_HASFOCUS; } } Display_CloseCinematics(); return m; } void Item_Init(itemDef_t *item) { memset(item, 0, sizeof(itemDef_t)); item->textscale = 0.55f; Window_Init(&item->window); } void Menu_HandleMouseMove(menuDef_t *menu, float x, float y) { int i, pass; qboolean focusSet = qfalse; itemDef_t *overItem; if (menu == NULL) { return; } if (!(menu->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { return; } if (itemCapture) { //Item_MouseMove(itemCapture, x, y); return; } if (g_waitingForKey || g_editingField) { return; } // FIXME: this is the whole issue of focus vs. mouse over.. // need a better overall solution as i don't like going through everything twice for (pass = 0; pass < 2; pass++) { for (i = 0; i < menu->itemCount; i++) { // turn off focus each item // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; if (!(menu->items[i]->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { continue; } // items can be enabled and disabled based on cvars if (menu->items[i]->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(menu->items[i], CVAR_ENABLE)) { continue; } if (menu->items[i]->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(menu->items[i], CVAR_SHOW)) { continue; } if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { if (pass == 1) { overItem = menu->items[i]; if (overItem->type == ITEM_TYPE_TEXT && overItem->text) { if (!Rect_ContainsPoint(Item_CorrectedTextRect(overItem), x, y)) { continue; } } // if we are over an item if (IsVisible(overItem->window.flags)) { // different one Item_MouseEnter(overItem, x, y); // Item_SetMouseOver(overItem, qtrue); // if item is not a decoration see if it can take focus if (!focusSet) { focusSet = Item_SetFocus(overItem, x, y); } } } } else if (menu->items[i]->window.flags & WINDOW_MOUSEOVER) { Item_MouseLeave(menu->items[i]); Item_SetMouseOver(menu->items[i], qfalse); } } } } void Menu_Paint(menuDef_t *menu, qboolean forcePaint) { int i; if (menu == NULL) { return; } if (!(menu->window.flags & WINDOW_VISIBLE) && !forcePaint) { return; } if (menu->window.ownerDrawFlags && DC->ownerDrawVisible && !DC->ownerDrawVisible(menu->window.ownerDrawFlags)) { return; } if (forcePaint) { menu->window.flags |= WINDOW_FORCED; } // draw the background if necessary if (menu->fullScreen) { // implies a background shader // FIXME: make sure we have a default shader if fullscreen is set with no background DC->drawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, menu->window.background ); } else if (menu->window.background) { // this allows a background shader without being full screen //UI_DrawHandlePic(menu->window.rect.x, menu->window.rect.y, menu->window.rect.w, menu->window.rect.h, menu->backgroundShader); } // paint the background and or border Window_Paint(&menu->window, menu->fadeAmount, menu->fadeClamp, menu->fadeCycle ); for (i = 0; i < menu->itemCount; i++) { Item_Paint(menu->items[i]); } if (debugMode) { vec4_t color; color[0] = color[2] = color[3] = 1; color[1] = 0; DC->drawRect(menu->window.rect.x, menu->window.rect.y, menu->window.rect.w, menu->window.rect.h, 1, color); } } /* =============== Item_ValidateTypeData =============== */ void Item_ValidateTypeData(itemDef_t *item) { if (item->typeData) { return; } if (item->type == ITEM_TYPE_LISTBOX) { item->typeData = UI_Alloc(sizeof(listBoxDef_t)); memset(item->typeData, 0, sizeof(listBoxDef_t)); } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD || item->type == ITEM_TYPE_YESNO || item->type == ITEM_TYPE_BIND || item->type == ITEM_TYPE_SLIDER || item->type == ITEM_TYPE_TEXT) { item->typeData = UI_Alloc(sizeof(editFieldDef_t)); memset(item->typeData, 0, sizeof(editFieldDef_t)); if (item->type == ITEM_TYPE_EDITFIELD) { if (!((editFieldDef_t *) item->typeData)->maxPaintChars) { ((editFieldDef_t *) item->typeData)->maxPaintChars = MAX_EDITFIELD; } } } else if (item->type == ITEM_TYPE_MULTI) { item->typeData = UI_Alloc(sizeof(multiDef_t)); } else if (item->type == ITEM_TYPE_MODEL) { item->typeData = UI_Alloc(sizeof(modelDef_t)); } } /* =============== Keyword Hash =============== */ #define KEYWORDHASH_SIZE 512 typedef struct keywordHash_s { char *keyword; qboolean (*func)(itemDef_t *item, int handle); struct keywordHash_s *next; } keywordHash_t; int KeywordHash_Key(char *keyword) { int register hash, i; hash = 0; for (i = 0; keyword[i] != '\0'; i++) { if (keyword[i] >= 'A' && keyword[i] <= 'Z') hash += (keyword[i] + ('a' - 'A')) * (119 + i); else hash += keyword[i] * (119 + i); } hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (KEYWORDHASH_SIZE-1); return hash; } void KeywordHash_Add(keywordHash_t *table[], keywordHash_t *key) { int hash; hash = KeywordHash_Key(key->keyword); /* if (table[hash]) { int collision = qtrue; } */ key->next = table[hash]; table[hash] = key; } keywordHash_t *KeywordHash_Find(keywordHash_t *table[], char *keyword) { keywordHash_t *key; int hash; hash = KeywordHash_Key(keyword); for (key = table[hash]; key; key = key->next) { if (!Q_stricmp(key->keyword, keyword)) return key; } return NULL; } /* =============== Item Keyword Parse functions =============== */ // name qboolean ItemParse_name( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &item->window.name)) { return qfalse; } return qtrue; } // name qboolean ItemParse_focusSound( itemDef_t *item, int handle ) { const char *temp; if (!PC_String_Parse(handle, &temp)) { return qfalse; } item->focusSound = DC->registerSound(temp, qfalse); return qtrue; } // text qboolean ItemParse_text( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &item->text)) { return qfalse; } return qtrue; } // group qboolean ItemParse_group( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &item->window.group)) { return qfalse; } return qtrue; } // asset_model qboolean ItemParse_asset_model( itemDef_t *item, int handle ) { const char *temp; modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (!PC_String_Parse(handle, &temp)) { return qfalse; } item->asset = DC->registerModel(temp); modelPtr->angle = rand() % 360; return qtrue; } // asset_shader qboolean ItemParse_asset_shader( itemDef_t *item, int handle ) { const char *temp; if (!PC_String_Parse(handle, &temp)) { return qfalse; } item->asset = DC->registerShaderNoMip(temp); return qtrue; } // model_origin qboolean ItemParse_model_origin( itemDef_t *item, int handle ) { modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (PC_Float_Parse(handle, &modelPtr->origin[0])) { if (PC_Float_Parse(handle, &modelPtr->origin[1])) { if (PC_Float_Parse(handle, &modelPtr->origin[2])) { return qtrue; } } } return qfalse; } // model_fovx qboolean ItemParse_model_fovx( itemDef_t *item, int handle ) { modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (!PC_Float_Parse(handle, &modelPtr->fov_x)) { return qfalse; } return qtrue; } // model_fovy qboolean ItemParse_model_fovy( itemDef_t *item, int handle ) { modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (!PC_Float_Parse(handle, &modelPtr->fov_y)) { return qfalse; } return qtrue; } // model_rotation qboolean ItemParse_model_rotation( itemDef_t *item, int handle ) { modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (!PC_Int_Parse(handle, &modelPtr->rotationSpeed)) { return qfalse; } return qtrue; } // model_angle qboolean ItemParse_model_angle( itemDef_t *item, int handle ) { modelDef_t *modelPtr; Item_ValidateTypeData(item); modelPtr = (modelDef_t*)item->typeData; if (!PC_Int_Parse(handle, &modelPtr->angle)) { return qfalse; } return qtrue; } // rect qboolean ItemParse_rect( itemDef_t *item, int handle ) { if (!PC_Rect_Parse(handle, &item->window.rectClient)) { return qfalse; } return qtrue; } // style qboolean ItemParse_style( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->window.style)) { return qfalse; } return qtrue; } // decoration qboolean ItemParse_decoration( itemDef_t *item, int handle ) { item->window.flags |= WINDOW_DECORATION; return qtrue; } // notselectable qboolean ItemParse_notselectable( itemDef_t *item, int handle ) { listBoxDef_t *listPtr; Item_ValidateTypeData(item); listPtr = (listBoxDef_t*)item->typeData; if (item->type == ITEM_TYPE_LISTBOX && listPtr) { listPtr->notselectable = qtrue; } return qtrue; } // manually wrapped qboolean ItemParse_wrapped( itemDef_t *item, int handle ) { item->window.flags |= WINDOW_WRAPPED; return qtrue; } // auto wrapped qboolean ItemParse_autowrapped( itemDef_t *item, int handle ) { item->window.flags |= WINDOW_AUTOWRAPPED; return qtrue; } // horizontalscroll qboolean ItemParse_horizontalscroll( itemDef_t *item, int handle ) { item->window.flags |= WINDOW_HORIZONTAL; return qtrue; } // type qboolean ItemParse_type( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->type)) { return qfalse; } Item_ValidateTypeData(item); return qtrue; } // elementwidth, used for listbox image elements // uses textalignx for storage qboolean ItemParse_elementwidth( itemDef_t *item, int handle ) { listBoxDef_t *listPtr; Item_ValidateTypeData(item); listPtr = (listBoxDef_t*)item->typeData; if (!PC_Float_Parse(handle, &listPtr->elementWidth)) { return qfalse; } return qtrue; } // elementheight, used for listbox image elements // uses textaligny for storage qboolean ItemParse_elementheight( itemDef_t *item, int handle ) { listBoxDef_t *listPtr; Item_ValidateTypeData(item); listPtr = (listBoxDef_t*)item->typeData; if (!PC_Float_Parse(handle, &listPtr->elementHeight)) { return qfalse; } return qtrue; } // feeder qboolean ItemParse_feeder( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->special)) { return qfalse; } return qtrue; } // elementtype, used to specify what type of elements a listbox contains // uses textstyle for storage qboolean ItemParse_elementtype( itemDef_t *item, int handle ) { listBoxDef_t *listPtr; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; listPtr = (listBoxDef_t*)item->typeData; if (!PC_Int_Parse(handle, &listPtr->elementStyle)) { return qfalse; } return qtrue; } // columns sets a number of columns and an x pos and width per.. qboolean ItemParse_columns( itemDef_t *item, int handle ) { int num, i; listBoxDef_t *listPtr; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; listPtr = (listBoxDef_t*)item->typeData; if (PC_Int_Parse(handle, &num)) { if (num > MAX_LB_COLUMNS) { num = MAX_LB_COLUMNS; } listPtr->numColumns = num; for (i = 0; i < num; i++) { int pos, width, maxChars; if (PC_Int_Parse(handle, &pos) && PC_Int_Parse(handle, &width) && PC_Int_Parse(handle, &maxChars)) { listPtr->columnInfo[i].pos = pos; listPtr->columnInfo[i].width = width; listPtr->columnInfo[i].maxChars = maxChars; } else { return qfalse; } } } else { return qfalse; } return qtrue; } qboolean ItemParse_border( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->window.border)) { return qfalse; } return qtrue; } qboolean ItemParse_bordersize( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->window.borderSize)) { return qfalse; } return qtrue; } qboolean ItemParse_visible( itemDef_t *item, int handle ) { int i; if (!PC_Int_Parse(handle, &i)) { return qfalse; } if (i) { item->window.flags |= WINDOW_VISIBLE; } return qtrue; } qboolean ItemParse_ownerdraw( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->window.ownerDraw)) { return qfalse; } item->type = ITEM_TYPE_OWNERDRAW; return qtrue; } qboolean ItemParse_align( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->alignment)) { return qfalse; } return qtrue; } qboolean ItemParse_textalign( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->textalignment)) { return qfalse; } return qtrue; } qboolean ItemParse_textalignx( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->textalignx)) { return qfalse; } return qtrue; } qboolean ItemParse_textaligny( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->textaligny)) { return qfalse; } return qtrue; } qboolean ItemParse_textscale( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->textscale)) { return qfalse; } return qtrue; } qboolean ItemParse_textstyle( itemDef_t *item, int handle ) { if (!PC_Int_Parse(handle, &item->textStyle)) { return qfalse; } return qtrue; } qboolean ItemParse_backcolor( itemDef_t *item, int handle ) { int i; float f; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } item->window.backColor[i] = f; } return qtrue; } qboolean ItemParse_forecolor( itemDef_t *item, int handle ) { int i; float f; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } item->window.foreColor[i] = f; item->window.flags |= WINDOW_FORECOLORSET; } return qtrue; } qboolean ItemParse_bordercolor( itemDef_t *item, int handle ) { int i; float f; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } item->window.borderColor[i] = f; } return qtrue; } qboolean ItemParse_outlinecolor( itemDef_t *item, int handle ) { if (!PC_Color_Parse(handle, &item->window.outlineColor)){ return qfalse; } return qtrue; } qboolean ItemParse_background( itemDef_t *item, int handle ) { const char *temp; if (!PC_String_Parse(handle, &temp)) { return qfalse; } item->window.background = DC->registerShaderNoMip(temp); return qtrue; } qboolean ItemParse_cinematic( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &item->window.cinematicName)) { return qfalse; } return qtrue; } qboolean ItemParse_doubleClick( itemDef_t *item, int handle ) { listBoxDef_t *listPtr; Item_ValidateTypeData(item); if (!item->typeData) { return qfalse; } listPtr = (listBoxDef_t*)item->typeData; if (!PC_Script_Parse(handle, &listPtr->doubleClick)) { return qfalse; } return qtrue; } qboolean ItemParse_onFocus( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->onFocus)) { return qfalse; } return qtrue; } qboolean ItemParse_leaveFocus( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->leaveFocus)) { return qfalse; } return qtrue; } qboolean ItemParse_mouseEnter( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->mouseEnter)) { return qfalse; } return qtrue; } qboolean ItemParse_mouseExit( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->mouseExit)) { return qfalse; } return qtrue; } qboolean ItemParse_mouseEnterText( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->mouseEnterText)) { return qfalse; } return qtrue; } qboolean ItemParse_mouseExitText( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->mouseExitText)) { return qfalse; } return qtrue; } qboolean ItemParse_action( itemDef_t *item, int handle ) { if (!PC_Script_Parse(handle, &item->action)) { return qfalse; } return qtrue; } qboolean ItemParse_special( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->special)) { return qfalse; } return qtrue; } qboolean ItemParse_cvarTest( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &item->cvarTest)) { return qfalse; } return qtrue; } qboolean ItemParse_cvar( itemDef_t *item, int handle ) { editFieldDef_t *editPtr; Item_ValidateTypeData(item); if (!PC_String_Parse(handle, &item->cvar)) { return qfalse; } if (item->typeData) { editPtr = (editFieldDef_t*)item->typeData; editPtr->minVal = -1; editPtr->maxVal = -1; editPtr->defVal = -1; } return qtrue; } qboolean ItemParse_maxChars( itemDef_t *item, int handle ) { editFieldDef_t *editPtr; int maxChars; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; if (!PC_Int_Parse(handle, &maxChars)) { return qfalse; } editPtr = (editFieldDef_t*)item->typeData; editPtr->maxChars = maxChars; return qtrue; } qboolean ItemParse_maxPaintChars( itemDef_t *item, int handle ) { editFieldDef_t *editPtr; int maxChars; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; if (!PC_Int_Parse(handle, &maxChars)) { return qfalse; } editPtr = (editFieldDef_t*)item->typeData; editPtr->maxPaintChars = maxChars; return qtrue; } qboolean ItemParse_cvarFloat( itemDef_t *item, int handle ) { editFieldDef_t *editPtr; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; editPtr = (editFieldDef_t*)item->typeData; if (PC_String_Parse(handle, &item->cvar) && PC_Float_Parse(handle, &editPtr->defVal) && PC_Float_Parse(handle, &editPtr->minVal) && PC_Float_Parse(handle, &editPtr->maxVal)) { return qtrue; } return qfalse; } qboolean ItemParse_cvarStrList( itemDef_t *item, int handle ) { pc_token_t token; multiDef_t *multiPtr; int pass; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; multiPtr = (multiDef_t*)item->typeData; multiPtr->count = 0; multiPtr->strDef = qtrue; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (*token.string != '{') { return qfalse; } pass = 0; while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) { PC_SourceError(handle, "end of file inside menu item\n"); return qfalse; } if (*token.string == '}') { return qtrue; } if (*token.string == ',' || *token.string == ';') { continue; } if (pass == 0) { multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string); pass = 1; } else { multiPtr->cvarStr[multiPtr->count] = String_Alloc(token.string); pass = 0; multiPtr->count++; if (multiPtr->count >= MAX_MULTI_CVARS) { return qfalse; } } } return qfalse; // bk001205 - LCC missing return value } qboolean ItemParse_cvarFloatList( itemDef_t *item, int handle ) { pc_token_t token; multiDef_t *multiPtr; Item_ValidateTypeData(item); if (!item->typeData) return qfalse; multiPtr = (multiDef_t*)item->typeData; multiPtr->count = 0; multiPtr->strDef = qfalse; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (*token.string != '{') { return qfalse; } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) { PC_SourceError(handle, "end of file inside menu item\n"); return qfalse; } if (*token.string == '}') { return qtrue; } if (*token.string == ',' || *token.string == ';') { continue; } multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string); if (!PC_Float_Parse(handle, &multiPtr->cvarValue[multiPtr->count])) { return qfalse; } multiPtr->count++; if (multiPtr->count >= MAX_MULTI_CVARS) { return qfalse; } } return qfalse; // bk001205 - LCC missing return value } qboolean ItemParse_addColorRange( itemDef_t *item, int handle ) { colorRangeDef_t color; if (PC_Float_Parse(handle, &color.low) && PC_Float_Parse(handle, &color.high) && PC_Color_Parse(handle, &color.color) ) { if (item->numColors < MAX_COLOR_RANGES) { memcpy(&item->colorRanges[item->numColors], &color, sizeof(color)); item->numColors++; } return qtrue; } return qfalse; } qboolean ItemParse_ownerdrawFlag( itemDef_t *item, int handle ) { int i; if (!PC_Int_Parse(handle, &i)) { return qfalse; } item->window.ownerDrawFlags |= i; return qtrue; } qboolean ItemParse_enableCvar( itemDef_t *item, int handle ) { if (PC_Script_Parse(handle, &item->enableCvar)) { item->cvarFlags = CVAR_ENABLE; return qtrue; } return qfalse; } qboolean ItemParse_disableCvar( itemDef_t *item, int handle ) { if (PC_Script_Parse(handle, &item->enableCvar)) { item->cvarFlags = CVAR_DISABLE; return qtrue; } return qfalse; } qboolean ItemParse_showCvar( itemDef_t *item, int handle ) { if (PC_Script_Parse(handle, &item->enableCvar)) { item->cvarFlags = CVAR_SHOW; return qtrue; } return qfalse; } qboolean ItemParse_hideCvar( itemDef_t *item, int handle ) { if (PC_Script_Parse(handle, &item->enableCvar)) { item->cvarFlags = CVAR_HIDE; return qtrue; } return qfalse; } keywordHash_t itemParseKeywords[] = { {"name", ItemParse_name, NULL}, {"text", ItemParse_text, NULL}, {"group", ItemParse_group, NULL}, {"asset_model", ItemParse_asset_model, NULL}, {"asset_shader", ItemParse_asset_shader, NULL}, {"model_origin", ItemParse_model_origin, NULL}, {"model_fovx", ItemParse_model_fovx, NULL}, {"model_fovy", ItemParse_model_fovy, NULL}, {"model_rotation", ItemParse_model_rotation, NULL}, {"model_angle", ItemParse_model_angle, NULL}, {"rect", ItemParse_rect, NULL}, {"style", ItemParse_style, NULL}, {"decoration", ItemParse_decoration, NULL}, {"notselectable", ItemParse_notselectable, NULL}, {"wrapped", ItemParse_wrapped, NULL}, {"autowrapped", ItemParse_autowrapped, NULL}, {"horizontalscroll", ItemParse_horizontalscroll, NULL}, {"type", ItemParse_type, NULL}, {"elementwidth", ItemParse_elementwidth, NULL}, {"elementheight", ItemParse_elementheight, NULL}, {"feeder", ItemParse_feeder, NULL}, {"elementtype", ItemParse_elementtype, NULL}, {"columns", ItemParse_columns, NULL}, {"border", ItemParse_border, NULL}, {"bordersize", ItemParse_bordersize, NULL}, {"visible", ItemParse_visible, NULL}, {"ownerdraw", ItemParse_ownerdraw, NULL}, {"align", ItemParse_align, NULL}, {"textalign", ItemParse_textalign, NULL}, {"textalignx", ItemParse_textalignx, NULL}, {"textaligny", ItemParse_textaligny, NULL}, {"textscale", ItemParse_textscale, NULL}, {"textstyle", ItemParse_textstyle, NULL}, {"backcolor", ItemParse_backcolor, NULL}, {"forecolor", ItemParse_forecolor, NULL}, {"bordercolor", ItemParse_bordercolor, NULL}, {"outlinecolor", ItemParse_outlinecolor, NULL}, {"background", ItemParse_background, NULL}, {"onFocus", ItemParse_onFocus, NULL}, {"leaveFocus", ItemParse_leaveFocus, NULL}, {"mouseEnter", ItemParse_mouseEnter, NULL}, {"mouseExit", ItemParse_mouseExit, NULL}, {"mouseEnterText", ItemParse_mouseEnterText, NULL}, {"mouseExitText", ItemParse_mouseExitText, NULL}, {"action", ItemParse_action, NULL}, {"special", ItemParse_special, NULL}, {"cvar", ItemParse_cvar, NULL}, {"maxChars", ItemParse_maxChars, NULL}, {"maxPaintChars", ItemParse_maxPaintChars, NULL}, {"focusSound", ItemParse_focusSound, NULL}, {"cvarFloat", ItemParse_cvarFloat, NULL}, {"cvarStrList", ItemParse_cvarStrList, NULL}, {"cvarFloatList", ItemParse_cvarFloatList, NULL}, {"addColorRange", ItemParse_addColorRange, NULL}, {"ownerdrawFlag", ItemParse_ownerdrawFlag, NULL}, {"enableCvar", ItemParse_enableCvar, NULL}, {"cvarTest", ItemParse_cvarTest, NULL}, {"disableCvar", ItemParse_disableCvar, NULL}, {"showCvar", ItemParse_showCvar, NULL}, {"hideCvar", ItemParse_hideCvar, NULL}, {"cinematic", ItemParse_cinematic, NULL}, {"doubleclick", ItemParse_doubleClick, NULL}, {NULL, 0, NULL} }; keywordHash_t *itemParseKeywordHash[KEYWORDHASH_SIZE]; /* =============== Item_SetupKeywordHash =============== */ void Item_SetupKeywordHash(void) { int i; memset(itemParseKeywordHash, 0, sizeof(itemParseKeywordHash)); for (i = 0; itemParseKeywords[i].keyword; i++) { KeywordHash_Add(itemParseKeywordHash, &itemParseKeywords[i]); } } /* =============== Item_Parse =============== */ qboolean Item_Parse(int handle, itemDef_t *item) { pc_token_t token; keywordHash_t *key; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (*token.string != '{') { return qfalse; } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) { PC_SourceError(handle, "end of file inside menu item\n"); return qfalse; } if (*token.string == '}') { return qtrue; } key = KeywordHash_Find(itemParseKeywordHash, token.string); if (!key) { PC_SourceError(handle, "unknown menu item keyword %s", token.string); continue; } if ( !key->func(item, handle) ) { PC_SourceError(handle, "couldn't parse menu item keyword %s", token.string); return qfalse; } } return qfalse; // bk001205 - LCC missing return value } // Item_InitControls // init's special control types void Item_InitControls(itemDef_t *item) { if (item == NULL) { return; } if (item->type == ITEM_TYPE_LISTBOX) { listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; item->cursorPos = 0; if (listPtr) { listPtr->cursorPos = 0; listPtr->startPos = 0; listPtr->endPos = 0; listPtr->cursorPos = 0; } } } /* =============== Menu Keyword Parse functions =============== */ qboolean MenuParse_font( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_String_Parse(handle, &menu->font)) { return qfalse; } if (!DC->Assets.fontRegistered) { DC->registerFont(menu->font, 48, &DC->Assets.textFont); DC->Assets.fontRegistered = qtrue; } return qtrue; } qboolean MenuParse_name( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_String_Parse(handle, &menu->window.name)) { return qfalse; } if (Q_stricmp(menu->window.name, "main") == 0) { // default main as having focus //menu->window.flags |= WINDOW_HASFOCUS; } return qtrue; } qboolean MenuParse_fullscreen( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, (int*) &menu->fullScreen)) { // bk001206 - cast qboolean return qfalse; } return qtrue; } qboolean MenuParse_rect( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Rect_Parse(handle, &menu->window.rect)) { return qfalse; } return qtrue; } qboolean MenuParse_style( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &menu->window.style)) { return qfalse; } return qtrue; } qboolean MenuParse_visible( itemDef_t *item, int handle ) { int i; menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &i)) { return qfalse; } if (i) { menu->window.flags |= WINDOW_VISIBLE; } return qtrue; } qboolean MenuParse_onOpen( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Script_Parse(handle, &menu->onOpen)) { return qfalse; } return qtrue; } qboolean MenuParse_onClose( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Script_Parse(handle, &menu->onClose)) { return qfalse; } return qtrue; } qboolean MenuParse_onESC( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Script_Parse(handle, &menu->onESC)) { return qfalse; } return qtrue; } qboolean MenuParse_border( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &menu->window.border)) { return qfalse; } return qtrue; } qboolean MenuParse_borderSize( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Float_Parse(handle, &menu->window.borderSize)) { return qfalse; } return qtrue; } qboolean MenuParse_backcolor( itemDef_t *item, int handle ) { int i; float f; menuDef_t *menu = (menuDef_t*)item; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } menu->window.backColor[i] = f; } return qtrue; } qboolean MenuParse_forecolor( itemDef_t *item, int handle ) { int i; float f; menuDef_t *menu = (menuDef_t*)item; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } menu->window.foreColor[i] = f; menu->window.flags |= WINDOW_FORECOLORSET; } return qtrue; } qboolean MenuParse_bordercolor( itemDef_t *item, int handle ) { int i; float f; menuDef_t *menu = (menuDef_t*)item; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } menu->window.borderColor[i] = f; } return qtrue; } qboolean MenuParse_focuscolor( itemDef_t *item, int handle ) { int i; float f; menuDef_t *menu = (menuDef_t*)item; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } menu->focusColor[i] = f; } return qtrue; } qboolean MenuParse_disablecolor( itemDef_t *item, int handle ) { int i; float f; menuDef_t *menu = (menuDef_t*)item; for (i = 0; i < 4; i++) { if (!PC_Float_Parse(handle, &f)) { return qfalse; } menu->disableColor[i] = f; } return qtrue; } qboolean MenuParse_outlinecolor( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Color_Parse(handle, &menu->window.outlineColor)){ return qfalse; } return qtrue; } qboolean MenuParse_background( itemDef_t *item, int handle ) { const char *buff; menuDef_t *menu = (menuDef_t*)item; if (!PC_String_Parse(handle, &buff)) { return qfalse; } menu->window.background = DC->registerShaderNoMip(buff); return qtrue; } qboolean MenuParse_cinematic( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_String_Parse(handle, &menu->window.cinematicName)) { return qfalse; } return qtrue; } qboolean MenuParse_ownerdrawFlag( itemDef_t *item, int handle ) { int i; menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &i)) { return qfalse; } menu->window.ownerDrawFlags |= i; return qtrue; } qboolean MenuParse_ownerdraw( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &menu->window.ownerDraw)) { return qfalse; } return qtrue; } // decoration qboolean MenuParse_popup( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; menu->window.flags |= WINDOW_POPUP; return qtrue; } qboolean MenuParse_outOfBounds( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; menu->window.flags |= WINDOW_OOB_CLICK; return qtrue; } qboolean MenuParse_soundLoop( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_String_Parse(handle, &menu->soundName)) { return qfalse; } return qtrue; } qboolean MenuParse_fadeClamp( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Float_Parse(handle, &menu->fadeClamp)) { return qfalse; } return qtrue; } qboolean MenuParse_fadeAmount( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Float_Parse(handle, &menu->fadeAmount)) { return qfalse; } return qtrue; } qboolean MenuParse_fadeCycle( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (!PC_Int_Parse(handle, &menu->fadeCycle)) { return qfalse; } return qtrue; } qboolean MenuParse_itemDef( itemDef_t *item, int handle ) { menuDef_t *menu = (menuDef_t*)item; if (menu->itemCount < MAX_MENUITEMS) { menu->items[menu->itemCount] = UI_Alloc(sizeof(itemDef_t)); Item_Init(menu->items[menu->itemCount]); if (!Item_Parse(handle, menu->items[menu->itemCount])) { return qfalse; } Item_InitControls(menu->items[menu->itemCount]); menu->items[menu->itemCount++]->parent = menu; } return qtrue; } keywordHash_t menuParseKeywords[] = { {"font", MenuParse_font, NULL}, {"name", MenuParse_name, NULL}, {"fullscreen", MenuParse_fullscreen, NULL}, {"rect", MenuParse_rect, NULL}, {"style", MenuParse_style, NULL}, {"visible", MenuParse_visible, NULL}, {"onOpen", MenuParse_onOpen, NULL}, {"onClose", MenuParse_onClose, NULL}, {"onESC", MenuParse_onESC, NULL}, {"border", MenuParse_border, NULL}, {"borderSize", MenuParse_borderSize, NULL}, {"backcolor", MenuParse_backcolor, NULL}, {"forecolor", MenuParse_forecolor, NULL}, {"bordercolor", MenuParse_bordercolor, NULL}, {"focuscolor", MenuParse_focuscolor, NULL}, {"disablecolor", MenuParse_disablecolor, NULL}, {"outlinecolor", MenuParse_outlinecolor, NULL}, {"background", MenuParse_background, NULL}, {"ownerdraw", MenuParse_ownerdraw, NULL}, {"ownerdrawFlag", MenuParse_ownerdrawFlag, NULL}, {"outOfBoundsClick", MenuParse_outOfBounds, NULL}, {"soundLoop", MenuParse_soundLoop, NULL}, {"itemDef", MenuParse_itemDef, NULL}, {"cinematic", MenuParse_cinematic, NULL}, {"popup", MenuParse_popup, NULL}, {"fadeClamp", MenuParse_fadeClamp, NULL}, {"fadeCycle", MenuParse_fadeCycle, NULL}, {"fadeAmount", MenuParse_fadeAmount, NULL}, {NULL, 0, NULL} }; keywordHash_t *menuParseKeywordHash[KEYWORDHASH_SIZE]; /* =============== Menu_SetupKeywordHash =============== */ void Menu_SetupKeywordHash(void) { int i; memset(menuParseKeywordHash, 0, sizeof(menuParseKeywordHash)); for (i = 0; menuParseKeywords[i].keyword; i++) { KeywordHash_Add(menuParseKeywordHash, &menuParseKeywords[i]); } } /* =============== Menu_Parse =============== */ qboolean Menu_Parse(int handle, menuDef_t *menu) { pc_token_t token; keywordHash_t *key; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (*token.string != '{') { return qfalse; } while ( 1 ) { memset(&token, 0, sizeof(pc_token_t)); if (!trap_PC_ReadToken(handle, &token)) { PC_SourceError(handle, "end of file inside menu\n"); return qfalse; } if (*token.string == '}') { return qtrue; } key = KeywordHash_Find(menuParseKeywordHash, token.string); if (!key) { PC_SourceError(handle, "unknown menu keyword %s", token.string); continue; } if ( !key->func((itemDef_t*)menu, handle) ) { PC_SourceError(handle, "couldn't parse menu keyword %s", token.string); return qfalse; } } return qfalse; // bk001205 - LCC missing return value } /* =============== Menu_New =============== */ void Menu_New(int handle) { menuDef_t *menu = &Menus[menuCount]; if (menuCount < MAX_MENUS) { Menu_Init(menu); if (Menu_Parse(handle, menu)) { Menu_PostParse(menu); menuCount++; } } } int Menu_Count(void) { return menuCount; } void Menu_PaintAll(void) { int i; if (captureFunc) { captureFunc(captureData); } for (i = 0; i < Menu_Count(); i++) { Menu_Paint(&Menus[i], qfalse); } if (debugMode) { vec4_t v = {1, 1, 1, 1}; DC->drawText(5, 25, .5, v, va("fps: %f", DC->FPS), 0, 0, 0); } } void Menu_Reset(void) { menuCount = 0; } displayContextDef_t *Display_GetContext(void) { return DC; } #ifndef MISSIONPACK // bk001206 static float captureX; static float captureY; #endif void *Display_CaptureItem(int x, int y) { int i; for (i = 0; i < menuCount; i++) { // turn off focus each item // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; if (Rect_ContainsPoint(&Menus[i].window.rect, x, y)) { return &Menus[i]; } } return NULL; } // FIXME: qboolean Display_MouseMove(void *p, int x, int y) { int i; menuDef_t *menu = p; if (menu == NULL) { menu = Menu_GetFocused(); if (menu) { if (menu->window.flags & WINDOW_POPUP) { Menu_HandleMouseMove(menu, x, y); return qtrue; } } for (i = 0; i < menuCount; i++) { Menu_HandleMouseMove(&Menus[i], x, y); } } else { menu->window.rect.x += x; menu->window.rect.y += y; Menu_UpdatePosition(menu); } return qtrue; } int Display_CursorType(int x, int y) { int i; for (i = 0; i < menuCount; i++) { rectDef_t r2; r2.x = Menus[i].window.rect.x - 3; r2.y = Menus[i].window.rect.y - 3; r2.w = r2.h = 7; if (Rect_ContainsPoint(&r2, x, y)) { return CURSOR_SIZER; } } return CURSOR_ARROW; } void Display_HandleKey(int key, qboolean down, int x, int y) { menuDef_t *menu = Display_CaptureItem(x, y); if (menu == NULL) { menu = Menu_GetFocused(); } if (menu) { Menu_HandleKey(menu, key, down ); } } static void Window_CacheContents(windowDef_t *window) { if (window) { if (window->cinematicName) { int cin = DC->playCinematic(window->cinematicName, 0, 0, 0, 0); DC->stopCinematic(cin); } } } static void Item_CacheContents(itemDef_t *item) { if (item) { Window_CacheContents(&item->window); } } static void Menu_CacheContents(menuDef_t *menu) { if (menu) { int i; Window_CacheContents(&menu->window); for (i = 0; i < menu->itemCount; i++) { Item_CacheContents(menu->items[i]); } if (menu->soundName && *menu->soundName) { DC->registerSound(menu->soundName, qfalse); } } } void Display_CacheAll(void) { int i; for (i = 0; i < menuCount; i++) { Menu_CacheContents(&Menus[i]); } } static qboolean Menu_OverActiveItem(menuDef_t *menu, float x, float y) { if (menu && menu->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED)) { if (Rect_ContainsPoint(&menu->window.rect, x, y)) { int i; for (i = 0; i < menu->itemCount; i++) { // turn off focus each item // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; if (!(menu->items[i]->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { continue; } if (menu->items[i]->window.flags & WINDOW_DECORATION) { continue; } if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { itemDef_t *overItem = menu->items[i]; if (overItem->type == ITEM_TYPE_TEXT && overItem->text) { if (Rect_ContainsPoint(Item_CorrectedTextRect(overItem), x, y)) { return qtrue; } else { continue; } } else { return qtrue; } } } } } return qfalse; } openarena_0.8.8.orig/code/ui/ui.q3asm0000644000175000017500000000020711656310265016140 0ustar smcvsmcv-o "\quake3\missionpack\vm\ui" ui_main ..\ui_syscalls ui_atoms ui_players ui_util ui_shared ui_gameinfo bg_misc bg_lib q_math q_shared openarena_0.8.8.orig/code/ui/ui_gameinfo.c0000644000175000017500000001774411656310265017221 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // gameinfo.c // #include "ui_local.h" // // arena and bot info // int ui_numBots; static char *ui_botInfos[MAX_BOTS]; static int ui_numArenas; static char *ui_arenaInfos[MAX_ARENAS]; #ifndef MISSIONPACK // bk001206 static int ui_numSinglePlayerArenas; static int ui_numSpecialSinglePlayerArenas; #endif /* =============== UI_ParseInfos =============== */ int UI_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "" ); } Info_SetValueForKey( info, key, token ); } //NOTE: extra space for arena number infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); if (infos[count]) { strcpy(infos[count], info); count++; } } return count; } /* =============== UI_LoadArenasFromFile =============== */ static void UI_LoadArenasFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_ARENAS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_ARENAS_TEXT ) { trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_ARENAS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] ); } /* =============== UI_LoadArenas =============== */ void UI_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; char *type; ui_numArenas = 0; uiInfo.mapCount = 0; trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { UI_LoadArenasFromFile(arenasFile.string); } else { UI_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadArenasFromFile(filename); } trap_Print( va( "%i arenas parsed\n", ui_numArenas ) ); if (UI_OutOfMemory()) { trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n"); } for( n = 0; n < ui_numArenas; n++ ) { // determine type uiInfo.mapList[uiInfo.mapCount].cinematic = -1; uiInfo.mapList[uiInfo.mapCount].mapLoadName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "map")); uiInfo.mapList[uiInfo.mapCount].mapName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "longname")); uiInfo.mapList[uiInfo.mapCount].levelShot = -1; uiInfo.mapList[uiInfo.mapCount].imageName = String_Alloc(va("levelshots/%s", uiInfo.mapList[uiInfo.mapCount].mapLoadName)); uiInfo.mapList[uiInfo.mapCount].typeBits = 0; type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if( *type ) { if( strstr( type, "ffa" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); } if( strstr( type, "tourney" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_TOURNAMENT); } if( strstr( type, "ctf" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTF); } if( strstr( type, "oneflag" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_1FCTF); } if( strstr( type, "overload" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_OBELISK); } if( strstr( type, "harvester" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_HARVESTER); } if( strstr( type, "elimination" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_ELIMINATION); } if( strstr( type, "ctfelimination" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTF_ELIMINATION); } if( strstr( type, "lms" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_LMS); } if( strstr( type, "dd" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_DOUBLE_D); } } else { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); } uiInfo.mapCount++; if (uiInfo.mapCount >= MAX_MAPS) { break; } } } /* =============== UI_LoadBotsFromFile =============== */ static void UI_LoadBotsFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_BOTS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_BOTS_TEXT ) { trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_BOTS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); COM_Compress(buf); ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] ); } /* =============== UI_LoadBots =============== */ void UI_LoadBots( void ) { vmCvar_t botsFile; int numdirs; char filename[128]; char dirlist[1024]; char* dirptr; int i; int dirlen; ui_numBots = 0; trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); if( *botsFile.string ) { UI_LoadBotsFromFile(botsFile.string); } else { UI_LoadBotsFromFile("scripts/bots.txt"); } // get all bots from .bot files numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadBotsFromFile(filename); } trap_Print( va( "%i bots parsed\n", ui_numBots ) ); } /* =============== UI_GetBotInfoByNumber =============== */ char *UI_GetBotInfoByNumber( int num ) { if( num < 0 || num >= ui_numBots ) { trap_Print( va( S_COLOR_RED "Invalid bot number: %i\n", num ) ); return NULL; } return ui_botInfos[num]; } /* =============== UI_GetBotInfoByName =============== */ char *UI_GetBotInfoByName( const char *name ) { int n; char *value; for ( n = 0; n < ui_numBots ; n++ ) { value = Info_ValueForKey( ui_botInfos[n], "name" ); if ( !Q_stricmp( value, name ) ) { return ui_botInfos[n]; } } return NULL; } int UI_GetNumBots( void ) { return ui_numBots; } char *UI_GetBotNameByNumber( int num ) { char *info = UI_GetBotInfoByNumber(num); if (info) { return Info_ValueForKey( info, "name" ); } return "Sarge"; } openarena_0.8.8.orig/code/ui/ui_public.h0000644000175000017500000001031211656310265016677 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifndef __UI_PUBLIC_H__ #define __UI_PUBLIC_H__ #define UI_API_VERSION 6 typedef struct { connstate_t connState; int connectPacketCount; int clientNum; char servername[MAX_STRING_CHARS]; char updateInfoString[MAX_STRING_CHARS]; char messageString[MAX_STRING_CHARS]; } uiClientState_t; typedef enum { UI_ERROR, UI_PRINT, UI_MILLISECONDS, UI_CVAR_SET, UI_CVAR_VARIABLEVALUE, UI_CVAR_VARIABLESTRINGBUFFER, UI_CVAR_SETVALUE, UI_CVAR_RESET, UI_CVAR_CREATE, UI_CVAR_INFOSTRINGBUFFER, UI_ARGC, UI_ARGV, UI_CMD_EXECUTETEXT, UI_FS_FOPENFILE, UI_FS_READ, UI_FS_WRITE, UI_FS_FCLOSEFILE, UI_FS_GETFILELIST, UI_R_REGISTERMODEL, UI_R_REGISTERSKIN, UI_R_REGISTERSHADERNOMIP, UI_R_CLEARSCENE, UI_R_ADDREFENTITYTOSCENE, UI_R_ADDPOLYTOSCENE, UI_R_ADDLIGHTTOSCENE, UI_R_RENDERSCENE, UI_R_SETCOLOR, UI_R_DRAWSTRETCHPIC, UI_UPDATESCREEN, UI_CM_LERPTAG, UI_CM_LOADMODEL, UI_S_REGISTERSOUND, UI_S_STARTLOCALSOUND, UI_KEY_KEYNUMTOSTRINGBUF, UI_KEY_GETBINDINGBUF, UI_KEY_SETBINDING, UI_KEY_ISDOWN, UI_KEY_GETOVERSTRIKEMODE, UI_KEY_SETOVERSTRIKEMODE, UI_KEY_CLEARSTATES, UI_KEY_GETCATCHER, UI_KEY_SETCATCHER, UI_GETCLIPBOARDDATA, UI_GETGLCONFIG, UI_GETCLIENTSTATE, UI_GETCONFIGSTRING, UI_LAN_GETPINGQUEUECOUNT, UI_LAN_CLEARPING, UI_LAN_GETPING, UI_LAN_GETPINGINFO, UI_CVAR_REGISTER, UI_CVAR_UPDATE, UI_MEMORY_REMAINING, UI_GET_CDKEY, UI_SET_CDKEY, UI_R_REGISTERFONT, UI_R_MODELBOUNDS, UI_PC_ADD_GLOBAL_DEFINE, UI_PC_LOAD_SOURCE, UI_PC_FREE_SOURCE, UI_PC_READ_TOKEN, UI_PC_SOURCE_FILE_AND_LINE, UI_S_STOPBACKGROUNDTRACK, UI_S_STARTBACKGROUNDTRACK, UI_REAL_TIME, UI_LAN_GETSERVERCOUNT, UI_LAN_GETSERVERADDRESSSTRING, UI_LAN_GETSERVERINFO, UI_LAN_MARKSERVERVISIBLE, UI_LAN_UPDATEVISIBLEPINGS, UI_LAN_RESETPINGS, UI_LAN_LOADCACHEDSERVERS, UI_LAN_SAVECACHEDSERVERS, UI_LAN_ADDSERVER, UI_LAN_REMOVESERVER, UI_CIN_PLAYCINEMATIC, UI_CIN_STOPCINEMATIC, UI_CIN_RUNCINEMATIC, UI_CIN_DRAWCINEMATIC, UI_CIN_SETEXTENTS, UI_R_REMAP_SHADER, UI_VERIFY_CDKEY, UI_LAN_SERVERSTATUS, UI_LAN_GETSERVERPING, UI_LAN_SERVERISVISIBLE, UI_LAN_COMPARESERVERS, // 1.32 UI_FS_SEEK, UI_SET_PBCLSTATUS, UI_MEMSET = 100, UI_MEMCPY, UI_STRNCPY, UI_SIN, UI_COS, UI_ATAN2, UI_SQRT, UI_FLOOR, UI_CEIL } uiImport_t; typedef enum { UIMENU_NONE, UIMENU_MAIN, UIMENU_INGAME, UIMENU_NEED_CD, UIMENU_BAD_CD_KEY, UIMENU_TEAM, UIMENU_POSTGAME } uiMenuCommand_t; #define SORT_HOST 0 #define SORT_MAP 1 #define SORT_CLIENTS 2 #define SORT_GAME 3 #define SORT_PING 4 typedef enum { UI_GETAPIVERSION = 0, // system reserved UI_INIT, // void UI_Init( void ); UI_SHUTDOWN, // void UI_Shutdown( void ); UI_KEY_EVENT, // void UI_KeyEvent( int key ); UI_MOUSE_EVENT, // void UI_MouseEvent( int dx, int dy ); UI_REFRESH, // void UI_Refresh( int time ); UI_IS_FULLSCREEN, // qboolean UI_IsFullscreen( void ); UI_SET_ACTIVE_MENU, // void UI_SetActiveMenu( uiMenuCommand_t menu ); UI_CONSOLE_COMMAND, // qboolean UI_ConsoleCommand( int realTime ); UI_DRAW_CONNECT_SCREEN, // void UI_DrawConnectScreen( qboolean overlay ); UI_HASUNIQUECDKEY // if !overlay, the background will be drawn, otherwise it will be // overlayed over whatever the cgame has drawn. // a GetClientState syscall will be made to get the current strings } uiExport_t; #endif openarena_0.8.8.orig/code/ui/ui_syscalls.asm0000644000175000017500000000602411656310265017614 0ustar smcvsmcvcode equ trap_Error -1 equ trap_Print -2 equ trap_Milliseconds -3 equ trap_Cvar_Set -4 equ trap_Cvar_VariableValue -5 equ trap_Cvar_VariableStringBuffer -6 equ trap_Cvar_SetValue -7 equ trap_Cvar_Reset -8 equ trap_Cvar_Create -9 equ trap_Cvar_InfoStringBuffer -10 equ trap_Argc -11 equ trap_Argv -12 equ trap_Cmd_ExecuteText -13 equ trap_FS_FOpenFile -14 equ trap_FS_Read -15 equ trap_FS_Write -16 equ trap_FS_FCloseFile -17 equ trap_FS_GetFileList -18 equ trap_R_RegisterModel -19 equ trap_R_RegisterSkin -20 equ trap_R_RegisterShaderNoMip -21 equ trap_R_ClearScene -22 equ trap_R_AddRefEntityToScene -23 equ trap_R_AddPolyToScene -24 equ trap_R_AddLightToScene -25 equ trap_R_RenderScene -26 equ trap_R_SetColor -27 equ trap_R_DrawStretchPic -28 equ trap_UpdateScreen -29 equ trap_CM_LerpTag -30 equ trap_CM_LoadModel -31 equ trap_S_RegisterSound -32 equ trap_S_StartLocalSound -33 equ trap_Key_KeynumToStringBuf -34 equ trap_Key_GetBindingBuf -35 equ trap_Key_SetBinding -36 equ trap_Key_IsDown -37 equ trap_Key_GetOverstrikeMode -38 equ trap_Key_SetOverstrikeMode -39 equ trap_Key_ClearStates -40 equ trap_Key_GetCatcher -41 equ trap_Key_SetCatcher -42 equ trap_GetClipboardData -43 equ trap_GetGlconfig -44 equ trap_GetClientState -45 equ trap_GetConfigString -46 equ trap_LAN_GetPingQueueCount -47 equ trap_LAN_ClearPing -48 equ trap_LAN_GetPing -49 equ trap_LAN_GetPingInfo -50 equ trap_Cvar_Register -51 equ trap_Cvar_Update -52 equ trap_MemoryRemaining -53 equ trap_GetCDKey -54 equ trap_SetCDKey -55 equ trap_R_RegisterFont -56 equ trap_R_ModelBounds -57 equ trap_PC_AddGlobalDefine -58 equ trap_PC_LoadSource -59 equ trap_PC_FreeSource -60 equ trap_PC_ReadToken -61 equ trap_PC_SourceFileAndLine -62 equ trap_S_StopBackgroundTrack -63 equ trap_S_StartBackgroundTrack -64 equ trap_RealTime -65 equ trap_LAN_GetServerCount -66 equ trap_LAN_GetServerAddressString -67 equ trap_LAN_GetServerInfo -68 equ trap_LAN_MarkServerVisible -69 equ trap_LAN_UpdateVisiblePings -70 equ trap_LAN_ResetPings -71 equ trap_LAN_LoadCachedServers -72 equ trap_LAN_SaveCachedServers -73 equ trap_LAN_AddServer -74 equ trap_LAN_RemoveServer -75 equ trap_CIN_PlayCinematic -76 equ trap_CIN_StopCinematic -77 equ trap_CIN_RunCinematic -78 equ trap_CIN_DrawCinematic -79 equ trap_CIN_SetExtents -80 equ trap_R_RemapShader -81 equ trap_VerifyCDKey -82 equ trap_LAN_ServerStatus -83 equ trap_LAN_GetServerPing -84 equ trap_LAN_ServerIsVisible -85 equ trap_LAN_CompareServers -86 equ trap_FS_Seek -87 equ trap_SetPbClStatus -88 equ memset -101 equ memcpy -102 equ strncpy -103 equ sin -104 equ cos -105 equ atan2 -106 equ sqrt -107 equ floor -108 equ ceil -109 openarena_0.8.8.orig/code/ui/ui_shared.h0000644000175000017500000004226611656310265016704 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef __UI_SHARED_H #define __UI_SHARED_H #include "../qcommon/q_shared.h" #include "../renderer/tr_types.h" #include "../client/keycodes.h" #include "../../ui/menudef.h" #define MAX_MENUNAME 32 #define MAX_ITEMTEXT 64 #define MAX_ITEMACTION 64 #define MAX_MENUDEFFILE 4096 #define MAX_MENUFILE 32768 #define MAX_MENUS 64 #define MAX_MENUITEMS 96 #define MAX_COLOR_RANGES 10 #define MAX_OPEN_MENUS 16 #define WINDOW_MOUSEOVER 0x00000001 // mouse is over it, non exclusive #define WINDOW_HASFOCUS 0x00000002 // has cursor focus, exclusive #define WINDOW_VISIBLE 0x00000004 // is visible #define WINDOW_GREY 0x00000008 // is visible but grey ( non-active ) #define WINDOW_DECORATION 0x00000010 // for decoration only, no mouse, keyboard, etc.. #define WINDOW_FADINGOUT 0x00000020 // fading out, non-active #define WINDOW_FADINGIN 0x00000040 // fading in #define WINDOW_MOUSEOVERTEXT 0x00000080 // mouse is over it, non exclusive #define WINDOW_INTRANSITION 0x00000100 // window is in transition #define WINDOW_FORECOLORSET 0x00000200 // forecolor was explicitly set ( used to color alpha images or not ) #define WINDOW_HORIZONTAL 0x00000400 // for list boxes and sliders, vertical is default this is set of horizontal #define WINDOW_LB_LEFTARROW 0x00000800 // mouse is over left/up arrow #define WINDOW_LB_RIGHTARROW 0x00001000 // mouse is over right/down arrow #define WINDOW_LB_THUMB 0x00002000 // mouse is over thumb #define WINDOW_LB_PGUP 0x00004000 // mouse is over page up #define WINDOW_LB_PGDN 0x00008000 // mouse is over page down #define WINDOW_ORBITING 0x00010000 // item is in orbit #define WINDOW_OOB_CLICK 0x00020000 // close on out of bounds click #define WINDOW_WRAPPED 0x00040000 // manually wrap text #define WINDOW_AUTOWRAPPED 0x00080000 // auto wrap text #define WINDOW_FORCED 0x00100000 // forced open #define WINDOW_POPUP 0x00200000 // popup #define WINDOW_BACKCOLORSET 0x00400000 // backcolor was explicitly set #define WINDOW_TIMEDVISIBLE 0x00800000 // visibility timing ( NOT implemented ) // CGAME cursor type bits #define CURSOR_NONE 0x00000001 #define CURSOR_ARROW 0x00000002 #define CURSOR_SIZER 0x00000004 #ifdef CGAME #define STRING_POOL_SIZE 128*1024 #else #define STRING_POOL_SIZE 384*1024 #endif #define MAX_STRING_HANDLES 4096 #define MAX_SCRIPT_ARGS 12 #define MAX_EDITFIELD 256 #define ART_FX_BASE "menu/art/fx_base" #define ART_FX_BLUE "menu/art/fx_blue" #define ART_FX_CYAN "menu/art/fx_cyan" #define ART_FX_GREEN "menu/art/fx_grn" #define ART_FX_RED "menu/art/fx_red" #define ART_FX_TEAL "menu/art/fx_teal" #define ART_FX_WHITE "menu/art/fx_white" #define ART_FX_YELLOW "menu/art/fx_yel" #define ASSET_GRADIENTBAR "ui/assets/gradientbar2.tga" #define ASSET_SCROLLBAR "ui/assets/scrollbar.tga" #define ASSET_SCROLLBAR_ARROWDOWN "ui/assets/scrollbar_arrow_dwn_a.tga" #define ASSET_SCROLLBAR_ARROWUP "ui/assets/scrollbar_arrow_up_a.tga" #define ASSET_SCROLLBAR_ARROWLEFT "ui/assets/scrollbar_arrow_left.tga" #define ASSET_SCROLLBAR_ARROWRIGHT "ui/assets/scrollbar_arrow_right.tga" #define ASSET_SCROLL_THUMB "ui/assets/scrollbar_thumb.tga" #define ASSET_SLIDER_BAR "ui/assets/slider2.tga" #define ASSET_SLIDER_THUMB "ui/assets/sliderbutt_1.tga" #define SCROLLBAR_SIZE 16.0 #define SLIDER_WIDTH 96.0 #define SLIDER_HEIGHT 16.0 #define SLIDER_THUMB_WIDTH 12.0 #define SLIDER_THUMB_HEIGHT 20.0 #define NUM_CROSSHAIRS 99 typedef struct { const char *command; const char *args[MAX_SCRIPT_ARGS]; } scriptDef_t; typedef struct { float x; // horiz position float y; // vert position float w; // width float h; // height; } rectDef_t; typedef rectDef_t Rectangle; // FIXME: do something to separate text vs window stuff typedef struct { Rectangle rect; // client coord rectangle Rectangle rectClient; // screen coord rectangle const char *name; // const char *group; // if it belongs to a group const char *cinematicName; // cinematic name int cinematic; // cinematic handle int style; // int border; // int ownerDraw; // ownerDraw style int ownerDrawFlags; // show flags for ownerdraw items float borderSize; // int flags; // visible, focus, mouseover, cursor Rectangle rectEffects; // for various effects Rectangle rectEffects2; // for various effects int offsetTime; // time based value for various effects int nextTime; // time next effect should cycle vec4_t foreColor; // text color vec4_t backColor; // border color vec4_t borderColor; // border color vec4_t outlineColor; // border color qhandle_t background; // background asset } windowDef_t; typedef windowDef_t Window; typedef struct { vec4_t color; float low; float high; } colorRangeDef_t; // FIXME: combine flags into bitfields to save space // FIXME: consolidate all of the common stuff in one structure for menus and items // THINKABOUTME: is there any compelling reason not to have items contain items // and do away with a menu per say.. major issue is not being able to dynamically allocate // and destroy stuff.. Another point to consider is adding an alloc free call for vm's and have // the engine just allocate the pool for it based on a cvar // many of the vars are re-used for different item types, as such they are not always named appropriately // the benefits of c++ in DOOM will greatly help crap like this // FIXME: need to put a type ptr that points to specific type info per type // #define MAX_LB_COLUMNS 16 typedef struct columnInfo_s { int pos; int width; int maxChars; } columnInfo_t; typedef struct listBoxDef_s { int startPos; int endPos; int drawPadding; int cursorPos; float elementWidth; float elementHeight; int elementStyle; int numColumns; columnInfo_t columnInfo[MAX_LB_COLUMNS]; const char *doubleClick; qboolean notselectable; } listBoxDef_t; typedef struct editFieldDef_s { float minVal; // edit field limits float maxVal; // float defVal; // float range; // int maxChars; // for edit fields int maxPaintChars; // for edit fields int paintOffset; // } editFieldDef_t; #define MAX_MULTI_CVARS 32 typedef struct multiDef_s { const char *cvarList[MAX_MULTI_CVARS]; const char *cvarStr[MAX_MULTI_CVARS]; float cvarValue[MAX_MULTI_CVARS]; int count; qboolean strDef; } multiDef_t; typedef struct modelDef_s { int angle; vec3_t origin; float fov_x; float fov_y; int rotationSpeed; } modelDef_t; #define CVAR_ENABLE 0x00000001 #define CVAR_DISABLE 0x00000002 #define CVAR_SHOW 0x00000004 #define CVAR_HIDE 0x00000008 typedef struct itemDef_s { Window window; // common positional, border, style, layout info Rectangle textRect; // rectangle the text ( if any ) consumes int type; // text, button, radiobutton, checkbox, textfield, listbox, combo int alignment; // left center right int textalignment; // ( optional ) alignment for text within rect based on text width float textalignx; // ( optional ) text alignment x coord float textaligny; // ( optional ) text alignment x coord float textscale; // scale percentage from 72pts int textStyle; // ( optional ) style, normal and shadowed are it for now const char *text; // display text void *parent; // menu owner qhandle_t asset; // handle to asset const char *mouseEnterText; // mouse enter script const char *mouseExitText; // mouse exit script const char *mouseEnter; // mouse enter script const char *mouseExit; // mouse exit script const char *action; // select script const char *onFocus; // select script const char *leaveFocus; // select script const char *cvar; // associated cvar const char *cvarTest; // associated cvar for enable actions const char *enableCvar; // enable, disable, show, or hide based on value, this can contain a list int cvarFlags; // what type of action to take on cvarenables sfxHandle_t focusSound; int numColors; // number of color ranges colorRangeDef_t colorRanges[MAX_COLOR_RANGES]; float special; // used for feeder id's etc.. diff per type int cursorPos; // cursor position in characters void *typeData; // type specific data ptr's } itemDef_t; typedef struct { Window window; const char *font; // font qboolean fullScreen; // covers entire screen int itemCount; // number of items; int fontIndex; // int cursorItem; // which item as the cursor int fadeCycle; // float fadeClamp; // float fadeAmount; // const char *onOpen; // run when the menu is first opened const char *onClose; // run when the menu is closed const char *onESC; // run when the menu is closed const char *soundName; // background loop sound for menu vec4_t focusColor; // focus color for items vec4_t disableColor; // focus color for items itemDef_t *items[MAX_MENUITEMS]; // items this menu contains } menuDef_t; typedef struct { const char *fontStr; const char *cursorStr; const char *gradientStr; fontInfo_t textFont; fontInfo_t smallFont; fontInfo_t bigFont; qhandle_t cursor; qhandle_t gradientBar; qhandle_t scrollBarArrowUp; qhandle_t scrollBarArrowDown; qhandle_t scrollBarArrowLeft; qhandle_t scrollBarArrowRight; qhandle_t scrollBar; qhandle_t scrollBarThumb; qhandle_t buttonMiddle; qhandle_t buttonInside; qhandle_t solidBox; qhandle_t sliderBar; qhandle_t sliderThumb; sfxHandle_t menuEnterSound; sfxHandle_t menuExitSound; sfxHandle_t menuBuzzSound; sfxHandle_t itemFocusSound; float fadeClamp; int fadeCycle; float fadeAmount; float shadowX; float shadowY; vec4_t shadowColor; float shadowFadeClamp; qboolean fontRegistered; // player settings qhandle_t fxBasePic; qhandle_t fxPic[7]; qhandle_t crosshairShader[NUM_CROSSHAIRS]; } cachedAssets_t; typedef struct { const char *name; void (*handler) (itemDef_t *item, char** args); } commandDef_t; typedef struct { qhandle_t (*registerShaderNoMip) (const char *p); void (*setColor) (const vec4_t v); void (*drawHandlePic) (float x, float y, float w, float h, qhandle_t asset); void (*drawStretchPic) (float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void (*drawText) (float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ); int (*textWidth) (const char *text, float scale, int limit); int (*textHeight) (const char *text, float scale, int limit); qhandle_t (*registerModel) (const char *p); void (*modelBounds) (qhandle_t model, vec3_t min, vec3_t max); void (*fillRect) ( float x, float y, float w, float h, const vec4_t color); void (*drawRect) ( float x, float y, float w, float h, float size, const vec4_t color); void (*drawSides) (float x, float y, float w, float h, float size); void (*drawTopBottom) (float x, float y, float w, float h, float size); void (*clearScene) ( void ); void (*addRefEntityToScene) (const refEntity_t *re ); void (*renderScene) ( const refdef_t *fd ); void (*registerFont) (const char *pFontname, int pointSize, fontInfo_t *font); void (*ownerDrawItem) (float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); float (*getValue) (int ownerDraw); qboolean (*ownerDrawVisible) (int flags); void (*runScript)(char **p); void (*getTeamColor)(vec4_t *color); void (*getCVarString)(const char *cvar, char *buffer, int bufsize); float (*getCVarValue)(const char *cvar); void (*setCVar)(const char *cvar, const char *value); void (*drawTextWithCursor)(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style); void (*setOverstrikeMode)(qboolean b); qboolean (*getOverstrikeMode)( void ); void (*startLocalSound)( sfxHandle_t sfx, int channelNum ); qboolean (*ownerDrawHandleKey)(int ownerDraw, int flags, float *special, int key); int (*feederCount)(float feederID); const char *(*feederItemText)(float feederID, int index, int column, qhandle_t *handle); qhandle_t (*feederItemImage)(float feederID, int index); void (*feederSelection)(float feederID, int index); void (*keynumToStringBuf)( int keynum, char *buf, int buflen ); void (*getBindingBuf)( int keynum, char *buf, int buflen ); void (*setBinding)( int keynum, const char *binding ); void (*executeText)(int exec_when, const char *text ); void (*Error)(int level, const char *error, ...); void (*Print)(const char *msg, ...); void (*Pause)(qboolean b); int (*ownerDrawWidth)(int ownerDraw, float scale); sfxHandle_t (*registerSound)(const char *name, qboolean compressed); void (*startBackgroundTrack)( const char *intro, const char *loop); void (*stopBackgroundTrack)( void ); int (*playCinematic)(const char *name, float x, float y, float w, float h); void (*stopCinematic)(int handle); void (*drawCinematic)(int handle, float x, float y, float w, float h); void (*runCinematicFrame)(int handle); float yscale; float xscale; float bias; int realTime; int frameTime; int cursorx; int cursory; qboolean debug; cachedAssets_t Assets; glconfig_t glconfig; qhandle_t whiteShader; qhandle_t gradientImage; qhandle_t cursor; float FPS; } displayContextDef_t; const char *String_Alloc(const char *p); void String_Init( void ); void String_Report( void ); void Init_Display(displayContextDef_t *dc); void Display_ExpandMacros(char * buff); void Menu_Init(menuDef_t *menu); void Item_Init(itemDef_t *item); void Menu_PostParse(menuDef_t *menu); menuDef_t *Menu_GetFocused( void ); void Menu_HandleKey(menuDef_t *menu, int key, qboolean down); void Menu_HandleMouseMove(menuDef_t *menu, float x, float y); void Menu_ScrollFeeder(menuDef_t *menu, int feeder, qboolean down); qboolean Float_Parse(char **p, float *f); qboolean Color_Parse(char **p, vec4_t *c); qboolean Int_Parse(char **p, int *i); qboolean Rect_Parse(char **p, rectDef_t *r); qboolean String_Parse(char **p, const char **out); qboolean Script_Parse(char **p, const char **out); qboolean PC_Float_Parse(int handle, float *f); qboolean PC_Color_Parse(int handle, vec4_t *c); qboolean PC_Int_Parse(int handle, int *i); qboolean PC_Rect_Parse(int handle, rectDef_t *r); qboolean PC_String_Parse(int handle, const char **out); qboolean PC_Script_Parse(int handle, const char **out); int Menu_Count( void ); void Menu_New(int handle); void Menu_PaintAll( void ); menuDef_t *Menus_ActivateByName(const char *p); void Menu_Reset( void ); qboolean Menus_AnyFullScreenVisible( void ); void Menus_Activate(menuDef_t *menu); displayContextDef_t *Display_GetContext( void ); void *Display_CaptureItem(int x, int y); qboolean Display_MouseMove(void *p, int x, int y); int Display_CursorType(int x, int y); qboolean Display_KeyBindPending( void ); void Menus_OpenByName(const char *p); menuDef_t *Menus_FindByName(const char *p); void Menus_ShowByName(const char *p); void Menus_CloseByName(const char *p); void Display_HandleKey(int key, qboolean down, int x, int y); void LerpColor(vec4_t a, vec4_t b, vec4_t c, float t); void Menus_CloseAll( void ); void Menu_Paint(menuDef_t *menu, qboolean forcePaint); void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name); void Display_CacheAll( void ); void *UI_Alloc( int size ); void UI_InitMemory( void ); qboolean UI_OutOfMemory( void ); #ifndef BASEOA void Controls_GetConfig( void ); void Controls_SetConfig(qboolean restart); void Controls_SetDefaults( void ); #endif int trap_PC_AddGlobalDefine( char *define ); int trap_PC_LoadSource( const char *filename ); int trap_PC_FreeSource( int handle ); int trap_PC_ReadToken( int handle, pc_token_t *pc_token ); int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ); #endif openarena_0.8.8.orig/code/ui/ui_atoms.c0000644000175000017500000003353111656310265016547 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /********************************************************************** UI_ATOMS.C User interface building blocks and support functions. **********************************************************************/ #include "ui_local.h" qboolean m_entersound; // after a frame, so caching won't disrupt the sound void QDECL Com_Error( int level, const char *error, ... ) { va_list argptr; char text[1024]; va_start (argptr, error); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); trap_Error( va("%s", text) ); } void QDECL Com_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); trap_Print( va("%s", text) ); } qboolean newUI = qfalse; /* ================= UI_ClampCvar ================= */ float UI_ClampCvar( float min, float max, float value ) { if ( value < min ) return min; if ( value > max ) return max; return value; } /* ================= UI_StartDemoLoop ================= */ void UI_StartDemoLoop( void ) { trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" ); } #ifndef MISSIONPACK // bk001206 static void NeedCDAction( qboolean result ) { if ( !result ) { trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } } #endif // MISSIONPACK #ifndef MISSIONPACK // bk001206 static void NeedCDKeyAction( qboolean result ) { if ( !result ) { trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } } #endif // MISSIONPACK char *UI_Argv( int arg ) { static char buffer[MAX_STRING_CHARS]; trap_Argv( arg, buffer, sizeof( buffer ) ); return buffer; } char *UI_Cvar_VariableString( const char *var_name ) { static char buffer[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( var_name, buffer, sizeof( buffer ) ); return buffer; } void UI_SetBestScores(postGameInfo_t *newInfo, qboolean postGame) { trap_Cvar_Set("ui_scoreAccuracy", va("%i%%", newInfo->accuracy)); trap_Cvar_Set("ui_scoreImpressives", va("%i", newInfo->impressives)); trap_Cvar_Set("ui_scoreExcellents", va("%i", newInfo->excellents)); trap_Cvar_Set("ui_scoreDefends", va("%i", newInfo->defends)); trap_Cvar_Set("ui_scoreAssists", va("%i", newInfo->assists)); trap_Cvar_Set("ui_scoreGauntlets", va("%i", newInfo->gauntlets)); trap_Cvar_Set("ui_scoreScore", va("%i", newInfo->score)); trap_Cvar_Set("ui_scorePerfect", va("%i", newInfo->perfects)); trap_Cvar_Set("ui_scoreTeam", va("%i to %i", newInfo->redScore, newInfo->blueScore)); trap_Cvar_Set("ui_scoreBase", va("%i", newInfo->baseScore)); trap_Cvar_Set("ui_scoreTimeBonus", va("%i", newInfo->timeBonus)); trap_Cvar_Set("ui_scoreSkillBonus", va("%i", newInfo->skillBonus)); trap_Cvar_Set("ui_scoreShutoutBonus", va("%i", newInfo->shutoutBonus)); trap_Cvar_Set("ui_scoreTime", va("%02i:%02i", newInfo->time / 60, newInfo->time % 60)); trap_Cvar_Set("ui_scoreCaptures", va("%i", newInfo->captures)); if (postGame) { trap_Cvar_Set("ui_scoreAccuracy2", va("%i%%", newInfo->accuracy)); trap_Cvar_Set("ui_scoreImpressives2", va("%i", newInfo->impressives)); trap_Cvar_Set("ui_scoreExcellents2", va("%i", newInfo->excellents)); trap_Cvar_Set("ui_scoreDefends2", va("%i", newInfo->defends)); trap_Cvar_Set("ui_scoreAssists2", va("%i", newInfo->assists)); trap_Cvar_Set("ui_scoreGauntlets2", va("%i", newInfo->gauntlets)); trap_Cvar_Set("ui_scoreScore2", va("%i", newInfo->score)); trap_Cvar_Set("ui_scorePerfect2", va("%i", newInfo->perfects)); trap_Cvar_Set("ui_scoreTeam2", va("%i to %i", newInfo->redScore, newInfo->blueScore)); trap_Cvar_Set("ui_scoreBase2", va("%i", newInfo->baseScore)); trap_Cvar_Set("ui_scoreTimeBonus2", va("%i", newInfo->timeBonus)); trap_Cvar_Set("ui_scoreSkillBonus2", va("%i", newInfo->skillBonus)); trap_Cvar_Set("ui_scoreShutoutBonus2", va("%i", newInfo->shutoutBonus)); trap_Cvar_Set("ui_scoreTime2", va("%02i:%02i", newInfo->time / 60, newInfo->time % 60)); trap_Cvar_Set("ui_scoreCaptures2", va("%i", newInfo->captures)); } } void UI_LoadBestScores(const char *map, int game) { char fileName[MAX_QPATH]; fileHandle_t f; postGameInfo_t newInfo; memset(&newInfo, 0, sizeof(postGameInfo_t)); Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game); if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) { int size = 0; trap_FS_Read(&size, sizeof(int), f); if (size == sizeof(postGameInfo_t)) { trap_FS_Read(&newInfo, sizeof(postGameInfo_t), f); } trap_FS_FCloseFile(f); } UI_SetBestScores(&newInfo, qfalse); Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.dm_%d", map, game, (int)trap_Cvar_VariableValue("protocol")); uiInfo.demoAvailable = qfalse; if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) { uiInfo.demoAvailable = qtrue; trap_FS_FCloseFile(f); } } /* =============== UI_ClearScores =============== */ void UI_ClearScores(void) { char gameList[4096]; char *gameFile; int i, len, count, size; fileHandle_t f; postGameInfo_t newInfo; count = trap_FS_GetFileList( "games", "game", gameList, sizeof(gameList) ); size = sizeof(postGameInfo_t); memset(&newInfo, 0, size); if (count > 0) { gameFile = gameList; for ( i = 0; i < count; i++ ) { len = strlen(gameFile); if (trap_FS_FOpenFile(va("games/%s",gameFile), &f, FS_WRITE) >= 0) { trap_FS_Write(&size, sizeof(int), f); trap_FS_Write(&newInfo, size, f); trap_FS_FCloseFile(f); } gameFile += len + 1; } } UI_SetBestScores(&newInfo, qfalse); } static void UI_Cache_f( void ) { Display_CacheAll(); } /* ======================= UI_CalcPostGameStats ======================= */ static void UI_CalcPostGameStats( void ) { char map[MAX_QPATH]; char fileName[MAX_QPATH]; char info[MAX_INFO_STRING]; fileHandle_t f; int size, game, time, adjustedTime; postGameInfo_t oldInfo; postGameInfo_t newInfo; qboolean newHigh = qfalse; trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) ); game = atoi(Info_ValueForKey(info, "g_gametype")); // compose file name Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game); // see if we have one already memset(&oldInfo, 0, sizeof(postGameInfo_t)); if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) { // if so load it size = 0; trap_FS_Read(&size, sizeof(int), f); if (size == sizeof(postGameInfo_t)) { trap_FS_Read(&oldInfo, sizeof(postGameInfo_t), f); } trap_FS_FCloseFile(f); } newInfo.accuracy = atoi(UI_Argv(3)); newInfo.impressives = atoi(UI_Argv(4)); newInfo.excellents = atoi(UI_Argv(5)); newInfo.defends = atoi(UI_Argv(6)); newInfo.assists = atoi(UI_Argv(7)); newInfo.gauntlets = atoi(UI_Argv(8)); newInfo.baseScore = atoi(UI_Argv(9)); newInfo.perfects = atoi(UI_Argv(10)); newInfo.redScore = atoi(UI_Argv(11)); newInfo.blueScore = atoi(UI_Argv(12)); time = atoi(UI_Argv(13)); newInfo.captures = atoi(UI_Argv(14)); newInfo.time = (time - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000; adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game]; if (newInfo.time < adjustedTime) { newInfo.timeBonus = (adjustedTime - newInfo.time) * 10; } else { newInfo.timeBonus = 0; } if (newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0) { newInfo.shutoutBonus = 100; } else { newInfo.shutoutBonus = 0; } newInfo.skillBonus = trap_Cvar_VariableValue("g_spSkill"); if (newInfo.skillBonus <= 0) { newInfo.skillBonus = 1; } newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus; newInfo.score *= newInfo.skillBonus; // see if the score is higher for this one newHigh = (newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score); if (newHigh) { // if so write out the new one uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000; if (trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) { size = sizeof(postGameInfo_t); trap_FS_Write(&size, sizeof(int), f); trap_FS_Write(&newInfo, sizeof(postGameInfo_t), f); trap_FS_FCloseFile(f); } } if (newInfo.time < oldInfo.time) { uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000; } // put back all the ui overrides trap_Cvar_Set("capturelimit", UI_Cvar_VariableString("ui_saveCaptureLimit")); trap_Cvar_Set("fraglimit", UI_Cvar_VariableString("ui_saveFragLimit")); trap_Cvar_Set("cg_drawTimer", UI_Cvar_VariableString("ui_drawTimer")); trap_Cvar_Set("g_doWarmup", UI_Cvar_VariableString("ui_doWarmup")); trap_Cvar_Set("g_Warmup", UI_Cvar_VariableString("ui_Warmup")); trap_Cvar_Set("sv_pure", UI_Cvar_VariableString("ui_pure")); trap_Cvar_Set("g_friendlyFire", UI_Cvar_VariableString("ui_friendlyFire")); UI_SetBestScores(&newInfo, qtrue); UI_ShowPostGame(newHigh); } /* ================= UI_ConsoleCommand ================= */ qboolean UI_ConsoleCommand( int realTime ) { char *cmd; uiInfo.uiDC.frameTime = realTime - uiInfo.uiDC.realTime; uiInfo.uiDC.realTime = realTime; cmd = UI_Argv( 0 ); // ensure minimum menu data is available //Menu_Cache(); if ( Q_stricmp (cmd, "ui_test") == 0 ) { UI_ShowPostGame(qtrue); } if ( Q_stricmp (cmd, "ui_report") == 0 ) { UI_Report(); return qtrue; } if ( Q_stricmp (cmd, "ui_load") == 0 ) { UI_Load(); return qtrue; } if ( Q_stricmp (cmd, "remapShader") == 0 ) { if (trap_Argc() == 4) { char shader1[MAX_QPATH]; char shader2[MAX_QPATH]; char shader3[MAX_QPATH]; Q_strncpyz(shader1, UI_Argv(1), sizeof(shader1)); Q_strncpyz(shader2, UI_Argv(2), sizeof(shader2)); Q_strncpyz(shader3, UI_Argv(3), sizeof(shader3)); trap_R_RemapShader(shader1, shader2, shader3); return qtrue; } } if ( Q_stricmp (cmd, "postgame") == 0 ) { UI_CalcPostGameStats(); return qtrue; } if ( Q_stricmp (cmd, "ui_cache") == 0 ) { UI_Cache_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) { //UI_TeamOrdersMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) { //UI_CDKeyMenu_f(); return qtrue; } return qfalse; } /* ================= UI_Shutdown ================= */ void UI_Shutdown( void ) { } /* ================ UI_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) { // expect valid pointers #if 0 *x = *x * uiInfo.uiDC.scale + uiInfo.uiDC.bias; *y *= uiInfo.uiDC.scale; *w *= uiInfo.uiDC.scale; *h *= uiInfo.uiDC.scale; #endif *x *= uiInfo.uiDC.xscale; *y *= uiInfo.uiDC.yscale; *w *= uiInfo.uiDC.xscale; *h *= uiInfo.uiDC.yscale; } void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { qhandle_t hShader; hShader = trap_R_RegisterShaderNoMip( picname ); UI_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) { float s0; float s1; float t0; float t1; if( w < 0 ) { // flip about vertical w = -w; s0 = 1; s1 = 0; } else { s0 = 0; s1 = 1; } if( h < 0 ) { // flip about horizontal h = -h; t0 = 1; t1 = 0; } else { t0 = 0; t1 = 1; } UI_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader ); } /* ================ UI_FillRect Coordinates are 640*480 virtual values ================= */ void UI_FillRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); UI_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); trap_R_SetColor( NULL ); } void UI_DrawSides(float x, float y, float w, float h) { UI_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); trap_R_DrawStretchPic( x + w - 1, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); } void UI_DrawTopBottom(float x, float y, float w, float h) { UI_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); trap_R_DrawStretchPic( x, y + h - 1, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); } /* ================ UI_DrawRect Coordinates are 640*480 virtual values ================= */ void UI_DrawRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); UI_DrawTopBottom(x, y, width, height); UI_DrawSides(x, y, width, height); trap_R_SetColor( NULL ); } void UI_SetColor( const float *rgba ) { trap_R_SetColor( rgba ); } void UI_UpdateScreen( void ) { trap_UpdateScreen(); } void UI_DrawTextBox (int x, int y, int width, int lines) { UI_FillRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack ); UI_DrawRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite ); } qboolean UI_CursorInRect (int x, int y, int width, int height) { if (uiInfo.uiDC.cursorx < x || uiInfo.uiDC.cursory < y || uiInfo.uiDC.cursorx > x+width || uiInfo.uiDC.cursory > y+height) return qfalse; return qtrue; } openarena_0.8.8.orig/code/ui/ui_players.c0000644000175000017500000011651411656310265017106 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // ui_players.c #include "ui_local.h" #define UI_TIMER_GESTURE 2300 #define UI_TIMER_JUMP 1000 #define UI_TIMER_LAND 130 #define UI_TIMER_WEAPON_SWITCH 300 #define UI_TIMER_ATTACK 500 #define UI_TIMER_MUZZLE_FLASH 20 #define UI_TIMER_WEAPON_DELAY 250 #define JUMP_HEIGHT 56 #define SWINGSPEED 0.3f #define SPIN_SPEED 0.9f #define COAST_TIME 1000 static int dp_realtime; static float jumpHeight; sfxHandle_t weaponChangeSound; /* =============== UI_PlayerInfo_SetWeapon =============== */ static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) { gitem_t * item; char path[MAX_QPATH]; pi->currentWeapon = weaponNum; tryagain: pi->realWeapon = weaponNum; pi->weaponModel = 0; pi->barrelModel = 0; pi->flashModel = 0; if ( weaponNum == WP_NONE ) { return; } for ( item = bg_itemlist + 1; item->classname ; item++ ) { if ( item->giType != IT_WEAPON ) { continue; } if ( item->giTag == weaponNum ) { break; } } if ( item->classname ) { pi->weaponModel = trap_R_RegisterModel( item->world_model[0] ); } if( pi->weaponModel == 0 ) { if( weaponNum == WP_MACHINEGUN ) { weaponNum = WP_NONE; goto tryagain; } weaponNum = WP_MACHINEGUN; goto tryagain; } if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) { strcpy( path, item->world_model[0] ); COM_StripExtension(path, path, sizeof(path)); strcat( path, "_barrel.md3" ); pi->barrelModel = trap_R_RegisterModel( path ); } strcpy( path, item->world_model[0] ); COM_StripExtension(path, path, sizeof(path)); strcat( path, "_flash.md3" ); pi->flashModel = trap_R_RegisterModel( path ); switch( weaponNum ) { case WP_GAUNTLET: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_MACHINEGUN: MAKERGB( pi->flashDlightColor, 1, 1, 0 ); break; case WP_SHOTGUN: MAKERGB( pi->flashDlightColor, 1, 1, 0 ); break; case WP_GRENADE_LAUNCHER: MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f ); break; case WP_ROCKET_LAUNCHER: MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 ); break; case WP_LIGHTNING: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_RAILGUN: MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 ); break; case WP_PLASMAGUN: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_BFG: MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 ); break; case WP_GRAPPLING_HOOK: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; default: MAKERGB( pi->flashDlightColor, 1, 1, 1 ); break; } } /* =============== UI_ForceLegsAnim =============== */ static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) { pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( anim == LEGS_JUMP ) { pi->legsAnimationTimer = UI_TIMER_JUMP; } } /* =============== UI_SetLegsAnim =============== */ static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) { if ( pi->pendingLegsAnim ) { anim = pi->pendingLegsAnim; pi->pendingLegsAnim = 0; } UI_ForceLegsAnim( pi, anim ); } /* =============== UI_ForceTorsoAnim =============== */ static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) { pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( anim == TORSO_GESTURE ) { pi->torsoAnimationTimer = UI_TIMER_GESTURE; } if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) { pi->torsoAnimationTimer = UI_TIMER_ATTACK; } } /* =============== UI_SetTorsoAnim =============== */ static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) { if ( pi->pendingTorsoAnim ) { anim = pi->pendingTorsoAnim; pi->pendingTorsoAnim = 0; } UI_ForceTorsoAnim( pi, anim ); } /* =============== UI_TorsoSequencing =============== */ static void UI_TorsoSequencing( playerInfo_t *pi ) { int currentAnim; currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if ( pi->weapon != pi->currentWeapon ) { if ( currentAnim != TORSO_DROP ) { pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; UI_ForceTorsoAnim( pi, TORSO_DROP ); } } if ( pi->torsoAnimationTimer > 0 ) { return; } if( currentAnim == TORSO_GESTURE ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } if ( currentAnim == TORSO_DROP ) { UI_PlayerInfo_SetWeapon( pi, pi->weapon ); pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; UI_ForceTorsoAnim( pi, TORSO_RAISE ); return; } if ( currentAnim == TORSO_RAISE ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } } /* =============== UI_LegsSequencing =============== */ static void UI_LegsSequencing( playerInfo_t *pi ) { int currentAnim; currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; if ( pi->legsAnimationTimer > 0 ) { if ( currentAnim == LEGS_JUMP ) { jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP ); } return; } if ( currentAnim == LEGS_JUMP ) { UI_ForceLegsAnim( pi, LEGS_LAND ); pi->legsAnimationTimer = UI_TIMER_LAND; jumpHeight = 0; return; } if ( currentAnim == LEGS_LAND ) { UI_SetLegsAnim( pi, LEGS_IDLE ); return; } } /* ====================== UI_PositionEntityOnTag ====================== */ static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, clipHandle_t parentModel, char *tagName ) { int i; orientation_t lerped; // lerp the tag trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // cast away const because of compiler problems MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis ); entity->backlerp = parent->backlerp; } /* ====================== UI_PositionRotatedEntityOnTag ====================== */ static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, clipHandle_t parentModel, char *tagName ) { int i; orientation_t lerped; vec3_t tempAxis[3]; // lerp the tag trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // cast away const because of compiler problems MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis ); MatrixMultiply( lerped.axis, tempAxis, entity->axis ); } /* =============== UI_SetLerpFrameAnimation =============== */ static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { animation_t *anim; lf->animationNumber = newAnimation; newAnimation &= ~ANIM_TOGGLEBIT; if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) { trap_Error( va("Bad animation number: %i", newAnimation) ); } anim = &ci->animations[ newAnimation ]; lf->animation = anim; lf->animationTime = lf->frameTime + anim->initialLerp; } /* =============== UI_RunLerpFrame =============== */ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { int f; animation_t *anim; // see if the animation sequence is switching if ( newAnimation != lf->animationNumber || !lf->animation ) { UI_SetLerpFrameAnimation( ci, lf, newAnimation ); } // if we have passed the current frame, move it to // oldFrame and calculate a new frame if ( dp_realtime >= lf->frameTime ) { lf->oldFrame = lf->frame; lf->oldFrameTime = lf->frameTime; // get the next frame based on the animation anim = lf->animation; if ( dp_realtime < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; if ( f >= anim->numFrames ) { f -= anim->numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { f = anim->numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = dp_realtime; } } lf->frame = anim->firstFrame + f; if ( dp_realtime > lf->frameTime ) { lf->frameTime = dp_realtime; } } if ( lf->frameTime > dp_realtime + 200 ) { lf->frameTime = dp_realtime; } if ( lf->oldFrameTime > dp_realtime ) { lf->oldFrameTime = dp_realtime; } // calculate current lerp value if ( lf->frameTime == lf->oldFrameTime ) { lf->backlerp = 0; } else { lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); } } /* =============== UI_PlayerAnimation =============== */ static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp, int *torsoOld, int *torso, float *torsoBackLerp ) { // legs animation pi->legsAnimationTimer -= uiInfo.uiDC.frameTime; if ( pi->legsAnimationTimer < 0 ) { pi->legsAnimationTimer = 0; } UI_LegsSequencing( pi ); if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN ); } else { UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim ); } *legsOld = pi->legs.oldFrame; *legs = pi->legs.frame; *legsBackLerp = pi->legs.backlerp; // torso animation pi->torsoAnimationTimer -= uiInfo.uiDC.frameTime; if ( pi->torsoAnimationTimer < 0 ) { pi->torsoAnimationTimer = 0; } UI_TorsoSequencing( pi ); UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim ); *torsoOld = pi->torso.oldFrame; *torso = pi->torso.frame; *torsoBackLerp = pi->torso.backlerp; } /* ================== UI_SwingAngles ================== */ static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qboolean *swinging ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = uiInfo.uiDC.frameTime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = uiInfo.uiDC.frameTime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } } /* ====================== UI_MovedirAdjustment ====================== */ static float UI_MovedirAdjustment( playerInfo_t *pi ) { vec3_t relativeAngles; vec3_t moveVector; VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles ); AngleVectors( relativeAngles, moveVector, NULL, NULL ); if ( Q_fabs( moveVector[0] ) < 0.01 ) { moveVector[0] = 0.0; } if ( Q_fabs( moveVector[1] ) < 0.01 ) { moveVector[1] = 0.0; } if ( moveVector[1] == 0 && moveVector[0] > 0 ) { return 0; } if ( moveVector[1] < 0 && moveVector[0] > 0 ) { return 22; } if ( moveVector[1] < 0 && moveVector[0] == 0 ) { return 45; } if ( moveVector[1] < 0 && moveVector[0] < 0 ) { return -22; } if ( moveVector[1] == 0 && moveVector[0] < 0 ) { return 0; } if ( moveVector[1] > 0 && moveVector[0] < 0 ) { return 22; } if ( moveVector[1] > 0 && moveVector[0] == 0 ) { return -45; } return -22; } /* =============== UI_PlayerAngles =============== */ static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { vec3_t legsAngles, torsoAngles, headAngles; float dest; float adjust; VectorCopy( pi->viewAngles, headAngles ); headAngles[YAW] = AngleMod( headAngles[YAW] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { // if not standing still, always point all in the same direction pi->torso.yawing = qtrue; // always center pi->torso.pitching = qtrue; // always center pi->legs.yawing = qtrue; // always center } // adjust legs for movement dir adjust = UI_MovedirAdjustment( pi ); legsAngles[YAW] = headAngles[YAW] + adjust; torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust; // torso UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing ); UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing ); torsoAngles[YAW] = pi->torso.yawAngle; legsAngles[YAW] = pi->legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[PITCH] > 180 ) { dest = (-360 + headAngles[PITCH]) * 0.75; } else { dest = headAngles[PITCH] * 0.75; } UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles[PITCH] = pi->torso.pitchAngle; // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesToAxis( legsAngles, legs ); AnglesToAxis( torsoAngles, torso ); AnglesToAxis( headAngles, head ); } /* =============== UI_PlayerFloatSprite =============== */ static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) { refEntity_t ent; memset( &ent, 0, sizeof( ent ) ); VectorCopy( origin, ent.origin ); ent.origin[2] += 48; ent.reType = RT_SPRITE; ent.customShader = shader; ent.radius = 10; ent.renderfx = 0; trap_R_AddRefEntityToScene( &ent ); } /* ====================== UI_MachinegunSpinAngle ====================== */ float UI_MachinegunSpinAngle( playerInfo_t *pi ) { int delta; float angle; float speed; int torsoAnim; delta = dp_realtime - pi->barrelTime; if ( pi->barrelSpinning ) { angle = pi->barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = pi->barrelAngle + delta * speed; } torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if( torsoAnim == TORSO_ATTACK2 ) { torsoAnim = TORSO_ATTACK; } if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) { pi->barrelTime = dp_realtime; pi->barrelAngle = AngleMod( angle ); pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK); } return angle; } /* =============== UI_DrawPlayer =============== */ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) { refdef_t refdef; refEntity_t legs; refEntity_t torso; refEntity_t head; refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t origin; int renderfx; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; float len; float xx; if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) { return; } // this allows the ui to cache the player model on the main menu if (w == 0 || h == 0) { return; } dp_realtime = time; if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) { pi->weapon = pi->pendingWeapon; pi->lastWeapon = pi->pendingWeapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; if( pi->currentWeapon != pi->weapon ) { trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL ); } } UI_AdjustFrom640( &x, &y, &w, &h ); y -= jumpHeight; memset( &refdef, 0, sizeof( refdef ) ); memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); refdef.fov_y = atan2( refdef.height, xx ); refdef.fov_y *= ( 360 / (float)M_PI ); // calculate distance so the player nearly fills the box len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); refdef.time = dp_realtime; trap_R_ClearScene(); // get the rotation information UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; // // add the legs // legs.hModel = pi->legsModel; legs.customSkin = pi->legsSkin; VectorCopy( origin, legs.origin ); VectorCopy( origin, legs.lightingOrigin ); legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); trap_R_AddRefEntityToScene( &legs ); if (!legs.hModel) { return; } // // add the torso // torso.hModel = pi->torsoModel; if (!torso.hModel) { return; } torso.customSkin = pi->torsoSkin; VectorCopy( origin, torso.lightingOrigin ); UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso"); torso.renderfx = renderfx; trap_R_AddRefEntityToScene( &torso ); // // add the head // head.hModel = pi->headModel; if (!head.hModel) { return; } head.customSkin = pi->headSkin; VectorCopy( origin, head.lightingOrigin ); UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head"); head.renderfx = renderfx; trap_R_AddRefEntityToScene( &head ); // // add the gun // if ( pi->currentWeapon != WP_NONE ) { memset( &gun, 0, sizeof(gun) ); gun.hModel = pi->weaponModel; VectorCopy( origin, gun.lightingOrigin ); UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); gun.renderfx = renderfx; trap_R_AddRefEntityToScene( &gun ); } // // add the spinning barrel // if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { vec3_t angles; memset( &barrel, 0, sizeof(barrel) ); VectorCopy( origin, barrel.lightingOrigin ); barrel.renderfx = renderfx; barrel.hModel = pi->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = UI_MachinegunSpinAngle( pi ); if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { angles[PITCH] = angles[ROLL]; angles[ROLL] = 0; } AnglesToAxis( angles, barrel.axis ); UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); trap_R_AddRefEntityToScene( &barrel ); } // // add muzzle flash // if ( dp_realtime <= pi->muzzleFlashTime ) { if ( pi->flashModel ) { memset( &flash, 0, sizeof(flash) ); flash.hModel = pi->flashModel; VectorCopy( origin, flash.lightingOrigin ); UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); flash.renderfx = renderfx; trap_R_AddRefEntityToScene( &flash ); } // make a dlight for the flash if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], pi->flashDlightColor[1], pi->flashDlightColor[2] ); } } // // add the chat icon // if ( pi->chat ) { UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); } // // add an accent light // origin[0] -= 100; // + = behind, - = in front origin[1] += 100; // + = left, - = right origin[2] += 100; // + = above, - = below trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 ); origin[0] -= 100; origin[1] -= 100; origin[2] -= 100; trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 ); trap_R_RenderScene( &refdef ); } /* =============== UI_DrawPlayerII A less FOV stretched version for drawing on the main menu =============== */ void UI_DrawPlayerII( float x, float y, float w, float h, playerInfo_t *pi, int time ) { refdef_t refdef; refEntity_t legs; refEntity_t torso; refEntity_t head; refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t origin; int renderfx; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; float len; float xx; if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) { return; } // this allows the ui to cache the player model on the main menu if (w == 0 || h == 0) { return; } dp_realtime = time; if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) { pi->weapon = pi->pendingWeapon; pi->lastWeapon = pi->pendingWeapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; if( pi->currentWeapon != pi->weapon ) { trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL ); } } UI_AdjustFrom640( &x, &y, &w, &h ); y -= jumpHeight; memset( &refdef, 0, sizeof( refdef ) ); memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = (int)((float)refdef.width / 640.0f * 30.0f); xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); refdef.fov_y = atan2( refdef.height, xx ); refdef.fov_y *= ( 360 / (float)M_PI ); // calculate distance so the player nearly fills the box len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.93 ); origin[1] = 1.0 * ( mins[1] + maxs[1] ); origin[2] = -2.7 * ( mins[2] + maxs[2] ); refdef.time = dp_realtime; trap_R_ClearScene(); // get the rotation information UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); renderfx = RF_LIGHTING_ORIGIN; // // add the legs // legs.hModel = pi->legsModel; legs.customSkin = pi->legsSkin; VectorCopy( origin, legs.origin ); VectorCopy( origin, legs.lightingOrigin ); legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); trap_R_AddRefEntityToScene( &legs ); if (!legs.hModel) { return; } // // add the torso // torso.hModel = pi->torsoModel; if (!torso.hModel) { return; } torso.customSkin = pi->torsoSkin; VectorCopy( origin, torso.lightingOrigin ); UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso"); torso.renderfx = renderfx; trap_R_AddRefEntityToScene( &torso ); // // add the head // head.hModel = pi->headModel; if (!head.hModel) { return; } head.customSkin = pi->headSkin; VectorCopy( origin, head.lightingOrigin ); UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head"); head.renderfx = renderfx; trap_R_AddRefEntityToScene( &head ); // // add the gun // if ( pi->currentWeapon != WP_NONE ) { memset( &gun, 0, sizeof(gun) ); gun.hModel = pi->weaponModel; VectorCopy( origin, gun.lightingOrigin ); UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); gun.renderfx = renderfx; trap_R_AddRefEntityToScene( &gun ); } // // add the spinning barrel // if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { vec3_t angles; memset( &barrel, 0, sizeof(barrel) ); VectorCopy( origin, barrel.lightingOrigin ); barrel.renderfx = renderfx; barrel.hModel = pi->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = UI_MachinegunSpinAngle( pi ); if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { angles[PITCH] = angles[ROLL]; angles[ROLL] = 0; } AnglesToAxis( angles, barrel.axis ); UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); trap_R_AddRefEntityToScene( &barrel ); } // // add muzzle flash // if ( dp_realtime <= pi->muzzleFlashTime ) { if ( pi->flashModel ) { memset( &flash, 0, sizeof(flash) ); flash.hModel = pi->flashModel; VectorCopy( origin, flash.lightingOrigin ); UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); flash.renderfx = renderfx; trap_R_AddRefEntityToScene( &flash ); } // make a dlight for the flash if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], pi->flashDlightColor[1], pi->flashDlightColor[2] ); } } // // add the chat icon // if ( pi->chat ) { UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); } // // add an accent light // origin[0] -= 100; // + = behind, - = in front origin[1] += 100; // + = left, - = right origin[2] += 100; // + = above, - = below //trap_R_AddLightToScene( origin, 500, 0.3, 0.2, 0.8 ); origin[0] += 10; // + = behind, - = in front origin[1] += 80; // + = left, - = right origin[2] += 130; // + = above, - = below trap_R_AddLightToScene( origin, 250, 0.54, 0.89, 0.79 ); origin[0] -= 50; // + = behind, - = in front origin[1] -= 90; // + = left, - = right origin[2] -= 69; // + = above, - = below trap_R_AddLightToScene( origin, 350, 0.60, 0.03, 0.22 ); origin[0] -= 100; origin[1] -= 100; origin[2] -= 100; //trap_R_AddLightToScene( origin, 500, 0.8, 0.2, 0.1 ); // UI_ForceLegsAnim( pi, BOTH_POSE ); // leilei - pose hack // UI_ForceTorsoAnim( pi, BOTH_POSE ); trap_R_RenderScene( &refdef ); } /* ========================== UI_FileExists ========================== */ static qboolean UI_FileExists(const char *filename) { int len; len = trap_FS_FOpenFile( filename, NULL, FS_READ ); if (len>0) { return qtrue; } return qfalse; } /* ========================== UI_FindClientHeadFile ========================== */ static qboolean UI_FindClientHeadFile( char *filename, int length, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) { char *team, *headsFolder; int i; team = "default"; if ( headModelName[0] == '*' ) { headsFolder = "heads/"; headModelName++; } else { headsFolder = ""; } while(1) { for ( i = 0; i < 2; i++ ) { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext ); } if ( UI_FileExists( filename ) ) { return qtrue; } if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext ); } if ( UI_FileExists( filename ) ) { return qtrue; } if ( !teamName || !*teamName ) { break; } } // if tried the heads folder first if ( headsFolder[0] ) { break; } headsFolder = "heads/"; } return qfalse; } /* ========================== UI_RegisterClientSkin ========================== */ static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName , const char *teamName) { char filename[MAX_QPATH*2]; if (teamName && *teamName) { Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/lower_%s.skin", modelName, teamName, skinName ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName ); } pi->legsSkin = trap_R_RegisterSkin( filename ); if (!pi->legsSkin) { if (teamName && *teamName) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/lower_%s.skin", modelName, teamName, skinName ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower_%s.skin", modelName, skinName ); } pi->legsSkin = trap_R_RegisterSkin( filename ); } if (teamName && *teamName) { Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/upper_%s.skin", modelName, teamName, skinName ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName ); } pi->torsoSkin = trap_R_RegisterSkin( filename ); if (!pi->torsoSkin) { if (teamName && *teamName) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/upper_%s.skin", modelName, teamName, skinName ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper_%s.skin", modelName, skinName ); } pi->torsoSkin = trap_R_RegisterSkin( filename ); } if ( UI_FindClientHeadFile( filename, sizeof(filename), teamName, headModelName, headSkinName, "head", "skin" ) ) { pi->headSkin = trap_R_RegisterSkin( filename ); } if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) { return qfalse; } return qtrue; } /* ====================== UI_ParseAnimationFile ====================== */ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) { char *text_p, *prev; int len; int i; char *token; float fps; int skip; char text[20000]; fileHandle_t f; memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS ); // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { return qfalse; } if ( len >= ( sizeof( text ) - 1 ) ) { Com_Printf( "File %s too long\n", filename ); trap_FS_FCloseFile( f ); return qfalse; } trap_FS_Read( text, len, f ); text[len] = 0; trap_FS_FCloseFile( f ); COM_Compress(text); // parse the text text_p = text; skip = 0; // quite the compiler warning // read optional parameters while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } continue; } // if it is a number, start parsing animations if ( token[0] >= '0' && token[0] <= '9' ) { text_p = prev; // unget the token break; } Com_Printf( "unknown token '%s' is %s\n", token, filename ); } // read information for each frame for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].firstFrame = atoi( token ); // leg only frames are adjusted to not count the upper body only frames if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } if ( i >= LEGS_WALKCR ) { animations[i].firstFrame -= skip; } token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].numFrames = atoi( token ); token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].loopFrames = atoi( token ); token = COM_Parse( &text_p ); if ( !token ) { break; } fps = atof( token ); if ( fps == 0 ) { fps = 1; } animations[i].frameLerp = 1000 / fps; animations[i].initialLerp = 1000 / fps; } if ( i != MAX_ANIMATIONS ) { Com_Printf( "Error parsing animation file: %s\n", filename ); return qfalse; } return qtrue; } /* ========================== UI_RegisterClientModelname ========================== */ qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName, const char *headModelSkinName, const char *teamName ) { char modelName[MAX_QPATH]; char skinName[MAX_QPATH]; char headModelName[MAX_QPATH]; char headSkinName[MAX_QPATH]; char filename[MAX_QPATH]; char *slash; pi->torsoModel = 0; pi->headModel = 0; if ( !modelSkinName[0] ) { return qfalse; } Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) ); slash = strchr( modelName, '/' ); if ( !slash ) { // modelName did not include a skin name Q_strncpyz( skinName, "default", sizeof( skinName ) ); } else { Q_strncpyz( skinName, slash + 1, sizeof( skinName ) ); *slash = '\0'; } Q_strncpyz( headModelName, headModelSkinName, sizeof( headModelName ) ); slash = strchr( headModelName, '/' ); if ( !slash ) { // modelName did not include a skin name Q_strncpyz( headSkinName, "default", sizeof( skinName ) ); } else { Q_strncpyz( headSkinName, slash + 1, sizeof( skinName ) ); *slash = '\0'; } // load cmodels before models so filecache works Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); pi->legsModel = trap_R_RegisterModel( filename ); if ( !pi->legsModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName ); pi->legsModel = trap_R_RegisterModel( filename ); if ( !pi->legsModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); pi->torsoModel = trap_R_RegisterModel( filename ); if ( !pi->torsoModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName ); pi->torsoModel = trap_R_RegisterModel( filename ); if ( !pi->torsoModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } if (headModelName[0] == '*' ) { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headModelName ); } pi->headModel = trap_R_RegisterModel( filename ); if ( !pi->headModel && headModelName[0] != '*') { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName ); pi->headModel = trap_R_RegisterModel( filename ); } if (!pi->headModel) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } // if any skins failed to load, fall back to default if ( !UI_RegisterClientSkin( pi, modelName, skinName, headModelName, headSkinName, teamName) ) { if ( !UI_RegisterClientSkin( pi, modelName, "default", headModelName, "default", teamName ) ) { Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName ); return qfalse; } } // load the animations Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); if ( !UI_ParseAnimationFile( filename, pi->animations ) ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName ); if ( !UI_ParseAnimationFile( filename, pi->animations ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } } return qtrue; } /* =============== UI_PlayerInfo_SetModel =============== */ void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model, const char *headmodel, char *teamName ) { memset( pi, 0, sizeof(*pi) ); UI_RegisterClientModelname( pi, model, headmodel, teamName ); pi->weapon = WP_MACHINEGUN; pi->currentWeapon = pi->weapon; pi->lastWeapon = pi->weapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; pi->chat = qfalse; pi->newModel = qtrue; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); } /* =============== UI_PlayerInfo_SetInfo =============== */ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) { int currentAnim; weapon_t weaponNum; pi->chat = chat; // view angles VectorCopy( viewAngles, pi->viewAngles ); // move angles VectorCopy( moveAngles, pi->moveAngles ); if ( pi->newModel ) { pi->newModel = qfalse; jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); pi->legs.yawAngle = viewAngles[YAW]; pi->legs.yawing = qfalse; pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); pi->torso.yawAngle = viewAngles[YAW]; pi->torso.yawing = qfalse; if ( weaponNumber != -1 ) { pi->weapon = weaponNumber; pi->currentWeapon = weaponNumber; pi->lastWeapon = weaponNumber; pi->pendingWeapon = -1; pi->weaponTimer = 0; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); } return; } // weapon if ( weaponNumber == -1 ) { pi->pendingWeapon = -1; pi->weaponTimer = 0; } else if ( weaponNumber != WP_NONE ) { pi->pendingWeapon = weaponNumber; pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY; } weaponNum = pi->lastWeapon; pi->weapon = weaponNum; if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) { torsoAnim = legsAnim = BOTH_DEATH1; pi->weapon = pi->currentWeapon = WP_NONE; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); return; } // leg animation currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) { pi->pendingLegsAnim = legsAnim; } else if ( legsAnim != currentAnim ) { jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); } // torso animation if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) { if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { torsoAnim = TORSO_STAND2; } else { torsoAnim = TORSO_STAND; } } if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) { if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { torsoAnim = TORSO_ATTACK2; } else { torsoAnim = TORSO_ATTACK; } pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH; //FIXME play firing sound here } currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) { pi->pendingTorsoAnim = torsoAnim; } else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) { pi->pendingTorsoAnim = torsoAnim; } else if ( torsoAnim != currentAnim ) { pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); } } openarena_0.8.8.orig/code/ui/ui_syscalls.c0000644000175000017500000002676711656310265017276 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" // this file is only included when building a dll // syscalls.asm is included instead when building a qvm #ifdef Q3_VM #error "Do not use in VM build" #endif static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1; void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) { syscall = syscallptr; } int PASSFLOAT( float x ) { float floatTemp; floatTemp = x; return *(int *)&floatTemp; } void trap_Print( const char *string ) { syscall( UI_PRINT, string ); } void trap_Error( const char *string ) { syscall( UI_ERROR, string ); } int trap_Milliseconds( void ) { return syscall( UI_MILLISECONDS ); } void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags ) { syscall( UI_CVAR_REGISTER, cvar, var_name, value, flags ); } void trap_Cvar_Update( vmCvar_t *cvar ) { syscall( UI_CVAR_UPDATE, cvar ); } void trap_Cvar_Set( const char *var_name, const char *value ) { syscall( UI_CVAR_SET, var_name, value ); } float trap_Cvar_VariableValue( const char *var_name ) { int temp; temp = syscall( UI_CVAR_VARIABLEVALUE, var_name ); return (*(float*)&temp); } void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { syscall( UI_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize ); } void trap_Cvar_SetValue( const char *var_name, float value ) { syscall( UI_CVAR_SETVALUE, var_name, PASSFLOAT( value ) ); } void trap_Cvar_Reset( const char *name ) { syscall( UI_CVAR_RESET, name ); } void trap_Cvar_Create( const char *var_name, const char *var_value, int flags ) { syscall( UI_CVAR_CREATE, var_name, var_value, flags ); } void trap_Cvar_InfoStringBuffer( int bit, char *buffer, int bufsize ) { syscall( UI_CVAR_INFOSTRINGBUFFER, bit, buffer, bufsize ); } int trap_Argc( void ) { return syscall( UI_ARGC ); } void trap_Argv( int n, char *buffer, int bufferLength ) { syscall( UI_ARGV, n, buffer, bufferLength ); } void trap_Cmd_ExecuteText( int exec_when, const char *text ) { syscall( UI_CMD_EXECUTETEXT, exec_when, text ); } int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { return syscall( UI_FS_FOPENFILE, qpath, f, mode ); } void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { syscall( UI_FS_READ, buffer, len, f ); } void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { syscall( UI_FS_WRITE, buffer, len, f ); } void trap_FS_FCloseFile( fileHandle_t f ) { syscall( UI_FS_FCLOSEFILE, f ); } int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ) { return syscall( UI_FS_GETFILELIST, path, extension, listbuf, bufsize ); } int trap_FS_Seek( fileHandle_t f, long offset, int origin ) { return syscall( UI_FS_SEEK, f, offset, origin ); } qhandle_t trap_R_RegisterModel( const char *name ) { return syscall( UI_R_REGISTERMODEL, name ); } qhandle_t trap_R_RegisterSkin( const char *name ) { return syscall( UI_R_REGISTERSKIN, name ); } void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { syscall( UI_R_REGISTERFONT, fontName, pointSize, font ); } qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { return syscall( UI_R_REGISTERSHADERNOMIP, name ); } void trap_R_ClearScene( void ) { syscall( UI_R_CLEARSCENE ); } void trap_R_AddRefEntityToScene( const refEntity_t *re ) { syscall( UI_R_ADDREFENTITYTOSCENE, re ); } void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) { syscall( UI_R_ADDPOLYTOSCENE, hShader, numVerts, verts ); } void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { syscall( UI_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); } void trap_R_RenderScene( const refdef_t *fd ) { syscall( UI_R_RENDERSCENE, fd ); } void trap_R_SetColor( const float *rgba ) { syscall( UI_R_SETCOLOR, rgba ); } void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) { syscall( UI_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader ); } void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { syscall( UI_R_MODELBOUNDS, model, mins, maxs ); } void trap_UpdateScreen( void ) { syscall( UI_UPDATESCREEN ); } int trap_CM_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ) { return syscall( UI_CM_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName ); } void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) { syscall( UI_S_STARTLOCALSOUND, sfx, channelNum ); } sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { return syscall( UI_S_REGISTERSOUND, sample, compressed ); } void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) { syscall( UI_KEY_KEYNUMTOSTRINGBUF, keynum, buf, buflen ); } void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ) { syscall( UI_KEY_GETBINDINGBUF, keynum, buf, buflen ); } void trap_Key_SetBinding( int keynum, const char *binding ) { syscall( UI_KEY_SETBINDING, keynum, binding ); } qboolean trap_Key_IsDown( int keynum ) { return syscall( UI_KEY_ISDOWN, keynum ); } qboolean trap_Key_GetOverstrikeMode( void ) { return syscall( UI_KEY_GETOVERSTRIKEMODE ); } void trap_Key_SetOverstrikeMode( qboolean state ) { syscall( UI_KEY_SETOVERSTRIKEMODE, state ); } void trap_Key_ClearStates( void ) { syscall( UI_KEY_CLEARSTATES ); } int trap_Key_GetCatcher( void ) { return syscall( UI_KEY_GETCATCHER ); } void trap_Key_SetCatcher( int catcher ) { syscall( UI_KEY_SETCATCHER, catcher ); } void trap_GetClipboardData( char *buf, int bufsize ) { syscall( UI_GETCLIPBOARDDATA, buf, bufsize ); } void trap_GetClientState( uiClientState_t *state ) { syscall( UI_GETCLIENTSTATE, state ); } void trap_GetGlconfig( glconfig_t *glconfig ) { syscall( UI_GETGLCONFIG, glconfig ); } int trap_GetConfigString( int index, char* buff, int buffsize ) { return syscall( UI_GETCONFIGSTRING, index, buff, buffsize ); } int trap_LAN_GetServerCount( int source ) { return syscall( UI_LAN_GETSERVERCOUNT, source ); } void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) { syscall( UI_LAN_GETSERVERADDRESSSTRING, source, n, buf, buflen ); } void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { syscall( UI_LAN_GETSERVERINFO, source, n, buf, buflen ); } int trap_LAN_GetServerPing( int source, int n ) { return syscall( UI_LAN_GETSERVERPING, source, n ); } int trap_LAN_GetPingQueueCount( void ) { return syscall( UI_LAN_GETPINGQUEUECOUNT ); } int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen ) { return syscall( UI_LAN_SERVERSTATUS, serverAddress, serverStatus, maxLen ); } void trap_LAN_SaveCachedServers( void ) { syscall( UI_LAN_SAVECACHEDSERVERS ); } void trap_LAN_LoadCachedServers( void ) { syscall( UI_LAN_LOADCACHEDSERVERS ); } void trap_LAN_ResetPings(int n) { syscall( UI_LAN_RESETPINGS, n ); } void trap_LAN_ClearPing( int n ) { syscall( UI_LAN_CLEARPING, n ); } void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) { syscall( UI_LAN_GETPING, n, buf, buflen, pingtime ); } void trap_LAN_GetPingInfo( int n, char *buf, int buflen ) { syscall( UI_LAN_GETPINGINFO, n, buf, buflen ); } void trap_LAN_MarkServerVisible( int source, int n, qboolean visible ) { syscall( UI_LAN_MARKSERVERVISIBLE, source, n, visible ); } int trap_LAN_ServerIsVisible( int source, int n) { return syscall( UI_LAN_SERVERISVISIBLE, source, n ); } qboolean trap_LAN_UpdateVisiblePings( int source ) { return syscall( UI_LAN_UPDATEVISIBLEPINGS, source ); } int trap_LAN_AddServer(int source, const char *name, const char *addr) { return syscall( UI_LAN_ADDSERVER, source, name, addr ); } void trap_LAN_RemoveServer(int source, const char *addr) { syscall( UI_LAN_REMOVESERVER, source, addr ); } int trap_LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) { return syscall( UI_LAN_COMPARESERVERS, source, sortKey, sortDir, s1, s2 ); } int trap_MemoryRemaining( void ) { return syscall( UI_MEMORY_REMAINING ); } void trap_GetCDKey( char *buf, int buflen ) { syscall( UI_GET_CDKEY, buf, buflen ); } void trap_SetCDKey( char *buf ) { syscall( UI_SET_CDKEY, buf ); } int trap_PC_AddGlobalDefine( char *define ) { return syscall( UI_PC_ADD_GLOBAL_DEFINE, define ); } int trap_PC_LoadSource( const char *filename ) { return syscall( UI_PC_LOAD_SOURCE, filename ); } int trap_PC_FreeSource( int handle ) { return syscall( UI_PC_FREE_SOURCE, handle ); } int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { return syscall( UI_PC_READ_TOKEN, handle, pc_token ); } int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { return syscall( UI_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); } void trap_S_StopBackgroundTrack( void ) { syscall( UI_S_STOPBACKGROUNDTRACK ); } void trap_S_StartBackgroundTrack( const char *intro, const char *loop) { syscall( UI_S_STARTBACKGROUNDTRACK, intro, loop ); } int trap_RealTime(qtime_t *qtime) { return syscall( UI_REAL_TIME, qtime ); } // this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate) int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) { return syscall(UI_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits); } // stops playing the cinematic and ends it. should always return FMV_EOF // cinematics must be stopped in reverse order of when they are started e_status trap_CIN_StopCinematic(int handle) { return syscall(UI_CIN_STOPCINEMATIC, handle); } // will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached. e_status trap_CIN_RunCinematic (int handle) { return syscall(UI_CIN_RUNCINEMATIC, handle); } // draws the current frame void trap_CIN_DrawCinematic (int handle) { syscall(UI_CIN_DRAWCINEMATIC, handle); } // allows you to resize the animation dynamically void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) { syscall(UI_CIN_SETEXTENTS, handle, x, y, w, h); } void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { syscall( UI_R_REMAP_SHADER, oldShader, newShader, timeOffset ); } qboolean trap_VerifyCDKey( const char *key, const char *chksum) { return syscall( UI_VERIFY_CDKEY, key, chksum); } void trap_SetPbClStatus( int status ) { syscall( UI_SET_PBCLSTATUS, status ); } openarena_0.8.8.orig/code/ui/ui_main.c0000644000175000017500000054767311656310265016371 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= USER INTERFACE MAIN ======================================================================= */ // use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build //#define PRE_RELEASE_TADEMO #include "ui_local.h" uiInfo_t uiInfo; static const char *MonthAbbrev[] = { "Jan","Feb","Mar", "Apr","May","Jun", "Jul","Aug","Sep", "Oct","Nov","Dec" }; static const char *skillLevels[] = { "I Can Win", "Bring It On", "Hurt Me Plenty", "Hardcore", "Nightmare" }; static const int numSkillLevels = sizeof(skillLevels) / sizeof(const char*); /* *These sources are currently hardcoded in the engine. * *Empty options will not be displayed or circled to. */ static const char *netSources[] = { "Local", "", "Internet", "Favorites" }; static const int numNetSources = sizeof(netSources) / sizeof(const char*); static const serverFilter_t serverFilters[] = { {"All", "" }, {"OpenArena", "" }, {"Missionpack", "missionpack" }, {"Rocket Arena", "arena" }, {"Alliance", "alliance20" }, {"Weapons Factory Arena", "wfa" }, {"OSP", "osp" }, }; static const char *teamArenaGameTypes[] = { "FFA", "TOURNAMENT", "SP", "TEAM DM", "CTF", "1FCTF", "OVERLOAD", "HARVESTER", "ELIMINATION", "CTFELIMINATION", "LMS", "DOUBLE D", "DOMINATION" }; static int const numTeamArenaGameTypes = sizeof(teamArenaGameTypes) / sizeof(const char*); static const char *teamArenaGameNames[] = { "Free For All", "Tournament", "Single Player", "Team Deathmatch", "Capture the Flag", "One Flag CTF", "Overload", "Harvester", "Elimination", "CTF Elimination", "Last Man Standing", "Double Domination", "Domination" }; static int const numTeamArenaGameNames = sizeof(teamArenaGameNames) / sizeof(const char*); static const int numServerFilters = sizeof(serverFilters) / sizeof(serverFilter_t); static const char *sortKeys[] = { "Server Name", "Map Name", "Open Player Spots", "Game Type", "Ping Time" }; static const int numSortKeys = sizeof(sortKeys) / sizeof(const char*); static char* netnames[] = { "???", "UDP", NULL }; #ifndef MISSIONPACK // bk001206 static char quake3worldMessage[] = "Visit www.openarena.ws - News, Community, Events, Files"; #endif static int gamecodetoui[] = {4,2,3,0,5,1,6}; static int uitogamecode[] = {4,6,2,3,1,5,7}; static void UI_StartServerRefresh(qboolean full); static void UI_StopServerRefresh( void ); static void UI_DoServerRefresh( void ); static void UI_FeederSelection(float feederID, int index); static void UI_BuildServerDisplayList(qboolean force); static void UI_BuildServerStatus(qboolean force); static void UI_BuildFindPlayerList(qboolean force); static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 ); static int UI_MapCountByGameType(qboolean singlePlayer); static int UI_HeadCountByTeam( void ); static void UI_ParseGameInfo(const char *teamFile); static void UI_ParseTeamInfo(const char *teamFile); static const char *UI_SelectedMap(int index, int *actual); static const char *UI_SelectedHead(int index, int *actual); static int UI_GetIndexFromSelection(int actual); int ProcessNewUI( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ); /* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .qvm file ================ */ vmCvar_t ui_new; vmCvar_t ui_debug; vmCvar_t ui_initialized; vmCvar_t ui_teamArenaFirstRun; void _UI_Init( qboolean ); void _UI_Shutdown( void ); void _UI_KeyEvent( int key, qboolean down ); void _UI_MouseEvent( int dx, int dy ); void _UI_Refresh( int realtime ); qboolean _UI_IsFullscreen( void ); intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case UI_GETAPIVERSION: return UI_API_VERSION; case UI_INIT: _UI_Init(arg0); return 0; case UI_SHUTDOWN: _UI_Shutdown(); return 0; case UI_KEY_EVENT: _UI_KeyEvent( arg0, arg1 ); return 0; case UI_MOUSE_EVENT: _UI_MouseEvent( arg0, arg1 ); return 0; case UI_REFRESH: _UI_Refresh( arg0 ); return 0; case UI_IS_FULLSCREEN: return _UI_IsFullscreen(); case UI_SET_ACTIVE_MENU: _UI_SetActiveMenu( arg0 ); return 0; case UI_CONSOLE_COMMAND: return UI_ConsoleCommand(arg0); case UI_DRAW_CONNECT_SCREEN: UI_DrawConnectScreen( arg0 ); return 0; case UI_HASUNIQUECDKEY: // mod authors need to observe this return qtrue; // bk010117 - change this to qfalse for mods! } return -1; } void AssetCache( void ) { int n; //if (Assets.textFont == NULL) { //} //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND ); //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus)); uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); uiInfo.uiDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); uiInfo.uiDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); uiInfo.uiDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); uiInfo.uiDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); uiInfo.uiDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); uiInfo.uiDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); uiInfo.uiDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); uiInfo.uiDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR ); uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN ); uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP ); uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT ); uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT ); uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB ); uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR ); uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB ); for( n = 0; n < NUM_CROSSHAIRS; n++ ) { uiInfo.uiDC.Assets.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) ); } uiInfo.newHighScoreSound = trap_S_RegisterSound("sound/feedback/voc_newhighscore.wav", qfalse); } void _UI_DrawSides(float x, float y, float w, float h, float size) { UI_AdjustFrom640( &x, &y, &w, &h ); size *= uiInfo.uiDC.xscale; trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); } void _UI_DrawTopBottom(float x, float y, float w, float h, float size) { UI_AdjustFrom640( &x, &y, &w, &h ); size *= uiInfo.uiDC.yscale; trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); } /* ================ UI_DrawRect Coordinates are 640*480 virtual values ================= */ void _UI_DrawRect( float x, float y, float width, float height, float size, const float *color ) { trap_R_SetColor( color ); _UI_DrawTopBottom(x, y, width, height, size); _UI_DrawSides(x, y, width, height, size); trap_R_SetColor( NULL ); } int Text_Width(const char *text, float scale, int limit) { int count,len; float out; glyphInfo_t *glyph; float useScale; const char *s = text; fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; if (scale <= ui_smallFont.value) { font = &uiInfo.uiDC.Assets.smallFont; } else if (scale >= ui_bigFont.value) { font = &uiInfo.uiDC.Assets.bigFont; } useScale = scale * font->glyphScale; out = 0; if (text) { len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { if ( Q_IsColorString(s) ) { s += 2; continue; } else { glyph = &font->glyphs[(int)*s]; out += glyph->xSkip; s++; count++; } } } return out * useScale; } int Text_Height(const char *text, float scale, int limit) { int len, count; float max; glyphInfo_t *glyph; float useScale; const char *s = text; // bk001206 - unsigned fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; if (scale <= ui_smallFont.value) { font = &uiInfo.uiDC.Assets.smallFont; } else if (scale >= ui_bigFont.value) { font = &uiInfo.uiDC.Assets.bigFont; } useScale = scale * font->glyphScale; max = 0; if (text) { len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { if ( Q_IsColorString(s) ) { s += 2; continue; } else { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build if (max < glyph->height) { max = glyph->height; } s++; count++; } } } return max * useScale; } void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) { float w, h; w = width * scale; h = height * scale; UI_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); } void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) { int len, count; vec4_t newColor; glyphInfo_t *glyph; float useScale; fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; if (scale <= ui_smallFont.value) { font = &uiInfo.uiDC.Assets.smallFont; } else if (scale >= ui_bigFont.value) { font = &uiInfo.uiDC.Assets.bigFont; } useScale = scale * font->glyphScale; if (text) { const char *s = text; // bk001206 - unsigned trap_R_SetColor( color ); memcpy(&newColor[0], &color[0], sizeof(vec4_t)); len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; colorBlack[3] = newColor[3]; trap_R_SetColor( colorBlack ); Text_PaintChar(x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); trap_R_SetColor( newColor ); colorBlack[3] = 1.0; } Text_PaintChar(x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); x += (glyph->xSkip * useScale) + adjust; s++; count++; } } trap_R_SetColor( NULL ); } } void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { int len, count; vec4_t newColor; glyphInfo_t *glyph, *glyph2; float yadj; float useScale; fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; if (scale <= ui_smallFont.value) { font = &uiInfo.uiDC.Assets.smallFont; } else if (scale >= ui_bigFont.value) { font = &uiInfo.uiDC.Assets.bigFont; } useScale = scale * font->glyphScale; if (text) { const char *s = text; // bk001206 - unsigned trap_R_SetColor( color ); memcpy(&newColor[0], &color[0], sizeof(vec4_t)); len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; glyph2 = &font->glyphs[ (int) cursor]; // bk001206 - possible signed char while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; trap_R_SetColor( newColor ); s += 2; continue; } else { yadj = useScale * glyph->top; if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; colorBlack[3] = newColor[3]; trap_R_SetColor( colorBlack ); Text_PaintChar(x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); colorBlack[3] = 1.0; trap_R_SetColor( newColor ); } Text_PaintChar(x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); yadj = useScale * glyph2->top; if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { Text_PaintChar(x, y - yadj, glyph2->imageWidth, glyph2->imageHeight, useScale, glyph2->s, glyph2->t, glyph2->s2, glyph2->t2, glyph2->glyph); } x += (glyph->xSkip * useScale); s++; count++; } } // need to paint cursor at end of text if (cursorPos == len && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { yadj = useScale * glyph2->top; Text_PaintChar(x, y - yadj, glyph2->imageWidth, glyph2->imageHeight, useScale, glyph2->s, glyph2->t, glyph2->s2, glyph2->t2, glyph2->glyph); } trap_R_SetColor( NULL ); } } static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) { int len, count; vec4_t newColor; glyphInfo_t *glyph; if (text) { const char *s = text; // bk001206 - unsigned float max = *maxX; float useScale; fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; if (scale <= ui_smallFont.value) { font = &uiInfo.uiDC.Assets.smallFont; } else if (scale > ui_bigFont.value) { font = &uiInfo.uiDC.Assets.bigFont; } useScale = scale * font->glyphScale; trap_R_SetColor( color ); len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if (Text_Width(s, useScale, 1) + x > max) { *maxX = 0; break; } Text_PaintChar(x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); x += (glyph->xSkip * useScale) + adjust; *maxX = x; count++; s++; } } trap_R_SetColor( NULL ); } } void UI_ShowPostGame(qboolean newHigh) { trap_Cvar_Set ("cg_cameraOrbit", "0"); trap_Cvar_Set("cg_thirdPerson", "0"); uiInfo.soundHighScore = newHigh; _UI_SetActiveMenu(UIMENU_POSTGAME); } /* ================= _UI_Refresh ================= */ void UI_DrawCenteredPic(qhandle_t image, int w, int h) { int x, y; x = (SCREEN_WIDTH - w) / 2; y = (SCREEN_HEIGHT - h) / 2; UI_DrawHandlePic(x, y, w, h, image); } int frameCount = 0; int startTime; #define UI_FPS_FRAMES 4 void _UI_Refresh( int realtime ) { static int index; static int previousTimes[UI_FPS_FRAMES]; //if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) { // return; //} uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime; uiInfo.uiDC.realTime = realtime; previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime; index++; if ( index > UI_FPS_FRAMES ) { int i, total; // average multiple frames together to smooth changes out a bit total = 0; for ( i = 0 ; i < UI_FPS_FRAMES ; i++ ) { total += previousTimes[i]; } if ( !total ) { total = 1; } uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total; } UI_UpdateCvars(); if (Menu_Count() > 0) { // paint all the menus Menu_PaintAll(); // refresh server browser list UI_DoServerRefresh(); // refresh server status UI_BuildServerStatus(qfalse); // refresh find player list UI_BuildFindPlayerList(qfalse); } // draw cursor UI_SetColor( NULL ); if (Menu_Count() > 0) { UI_DrawHandlePic( uiInfo.uiDC.cursorx-16, uiInfo.uiDC.cursory-16, 32, 32, uiInfo.uiDC.Assets.cursor); } #ifndef NDEBUG if (uiInfo.uiDC.debug) { // cursor coordinates //FIXME //UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed ); } #endif } /* ================= _UI_Shutdown ================= */ void _UI_Shutdown( void ) { trap_LAN_SaveCachedServers(); } char *defaultMenu = NULL; char *GetMenuBuffer(const char *filename) { int len; fileHandle_t f; static char buf[MAX_MENUFILE]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) ); return defaultMenu; } if ( len >= MAX_MENUFILE ) { trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i\n", filename, len, MAX_MENUFILE ) ); trap_FS_FCloseFile( f ); return defaultMenu; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); //COM_Compress(buf); return buf; } qboolean Asset_Parse(int handle) { pc_token_t token; const char *tempStr; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "{") != 0) { return qfalse; } while ( 1 ) { memset(&token, 0, sizeof(pc_token_t)); if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "}") == 0) { return qtrue; } // font if (Q_stricmp(token.string, "font") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { return qfalse; } trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.textFont); uiInfo.uiDC.Assets.fontRegistered = qtrue; continue; } if (Q_stricmp(token.string, "smallFont") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { return qfalse; } trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont); continue; } if (Q_stricmp(token.string, "bigFont") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { return qfalse; } trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont); continue; } // gradientbar if (Q_stricmp(token.string, "gradientbar") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr); continue; } // enterMenuSound if (Q_stricmp(token.string, "menuEnterSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // exitMenuSound if (Q_stricmp(token.string, "menuExitSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // itemFocusSound if (Q_stricmp(token.string, "itemFocusSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // menuBuzzSound if (Q_stricmp(token.string, "menuBuzzSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } if (Q_stricmp(token.string, "cursor") == 0) { if (!PC_String_Parse(handle, &uiInfo.uiDC.Assets.cursorStr)) { return qfalse; } uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( uiInfo.uiDC.Assets.cursorStr); continue; } if (Q_stricmp(token.string, "fadeClamp") == 0) { if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeClamp)) { return qfalse; } continue; } if (Q_stricmp(token.string, "fadeCycle") == 0) { if (!PC_Int_Parse(handle, &uiInfo.uiDC.Assets.fadeCycle)) { return qfalse; } continue; } if (Q_stricmp(token.string, "fadeAmount") == 0) { if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeAmount)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowX") == 0) { if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowX)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowY") == 0) { if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowY)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowColor") == 0) { if (!PC_Color_Parse(handle, &uiInfo.uiDC.Assets.shadowColor)) { return qfalse; } uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3]; continue; } } return qfalse; } void Font_Report( void ) { int i; Com_Printf("Font Info\n"); Com_Printf("=========\n"); for ( i = 32; i < 96; i++) { Com_Printf("Glyph handle %i: %i\n", i, uiInfo.uiDC.Assets.textFont.glyphs[i].glyph); } } void UI_Report( void ) { String_Report(); //Font_Report(); } void UI_ParseMenu(const char *menuFile) { int handle; pc_token_t token; Com_Printf("Parsing menu file:%s\n", menuFile); handle = trap_PC_LoadSource(menuFile); if (!handle) { return; } while ( 1 ) { memset(&token, 0, sizeof(pc_token_t)); if (!trap_PC_ReadToken( handle, &token )) { break; } //if ( Q_stricmp( token, "{" ) ) { // Com_Printf( "Missing { in menu file\n" ); // break; //} //if ( menuCount == MAX_MENUS ) { // Com_Printf( "Too many menus!\n" ); // break; //} if ( token.string[0] == '}' ) { break; } if (Q_stricmp(token.string, "assetGlobalDef") == 0) { if (Asset_Parse(handle)) { continue; } else { break; } } if (Q_stricmp(token.string, "menudef") == 0) { // start a new menu Menu_New(handle); } } trap_PC_FreeSource(handle); } qboolean Load_Menu(int handle) { pc_token_t token; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (token.string[0] != '{') { return qfalse; } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) return qfalse; if ( token.string[0] == 0 ) { return qfalse; } if ( token.string[0] == '}' ) { return qtrue; } UI_ParseMenu(token.string); } return qfalse; } void UI_LoadMenus(const char *menuFile, qboolean reset) { pc_token_t token; int handle; int start; start = trap_Milliseconds(); handle = trap_PC_LoadSource( menuFile ); if (!handle) { trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); handle = trap_PC_LoadSource( "ui/menus.txt" ); if (!handle) { trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n") ); } } ui_new.integer = 1; if (reset) { Menu_Reset(); } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) break; if( token.string[0] == 0 || token.string[0] == '}') { break; } if ( token.string[0] == '}' ) { break; } if (Q_stricmp(token.string, "loadmenu") == 0) { if (Load_Menu(handle)) { continue; } else { break; } } } Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start); trap_PC_FreeSource( handle ); } void UI_Load(void) { char lastName[1024]; menuDef_t *menu = Menu_GetFocused(); char *menuSet = UI_Cvar_VariableString("ui_menuFiles"); if (menu && menu->window.name) { strcpy(lastName, menu->window.name); } if (menuSet == NULL || menuSet[0] == '\0') { menuSet = "ui/menus.txt"; } String_Init(); #ifdef PRE_RELEASE_TADEMO UI_ParseGameInfo("demogameinfo.txt"); #else UI_ParseGameInfo("gameinfo.txt"); UI_LoadArenas(); #endif UI_LoadMenus(menuSet, qtrue); Menus_CloseAll(); Menus_ActivateByName(lastName); } static const char *handicapValues[] = {"None","95","90","85","80","75","70","65","60","55","50","45","40","35","30","25","20","15","10","5",NULL}; #ifndef MISSIONPACK // bk001206 static int numHandicaps = sizeof(handicapValues) / sizeof(const char*); #endif static void UI_DrawHandicap(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i, h; h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); i = 20 - h / 5; Text_Paint(rect->x, rect->y, scale, color, handicapValues[i], 0, 0, textStyle); } static void UI_DrawClanName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_teamName"), 0, 0, textStyle); } static void UI_SetCapFragLimits(qboolean uiVars) { int cap = 5; int frag = 10; if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_OBELISK) { cap = 4; } else if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_HARVESTER) { cap = 15; } if (uiVars) { trap_Cvar_Set("ui_captureLimit", va("%d", cap)); trap_Cvar_Set("ui_fragLimit", va("%d", frag)); } else { trap_Cvar_Set("capturelimit", va("%d", cap)); trap_Cvar_Set("fraglimit", va("%d", frag)); } } // ui_gameType assumes gametype 0 is -1 ALL and will not show static void UI_DrawGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_gameType.integer].gameType, 0, 0, textStyle); } static void UI_DrawNetGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (ui_netGameType.integer < 0 || ui_netGameType.integer > uiInfo.numGameTypes) { trap_Cvar_Set("ui_netGameType", "0"); trap_Cvar_Set("ui_actualNetGameType", "0"); } Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_netGameType.integer].gameType , 0, 0, textStyle); } static void UI_DrawJoinGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (ui_joinGameType.integer < 0 || ui_joinGameType.integer > uiInfo.numJoinGameTypes) { trap_Cvar_Set("ui_joinGameType", "0"); } Text_Paint(rect->x, rect->y, scale, color, uiInfo.joinGameTypes[ui_joinGameType.integer].gameType , 0, 0, textStyle); } static int UI_TeamIndexFromName(const char *name) { int i; if (name && *name) { for (i = 0; i < uiInfo.teamCount; i++) { if (Q_stricmp(name, uiInfo.teamList[i].teamName) == 0) { return i; } } } return 0; } static void UI_DrawClanLogo(rectDef_t *rect, float scale, vec4_t color) { int i; i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (i >= 0 && i < uiInfo.teamCount) { trap_R_SetColor( color ); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon); trap_R_SetColor(NULL); } } static void UI_DrawClanCinematic(rectDef_t *rect, float scale, vec4_t color) { int i; i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (i >= 0 && i < uiInfo.teamCount) { if (uiInfo.teamList[i].cinematic >= -2) { if (uiInfo.teamList[i].cinematic == -1) { uiInfo.teamList[i].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.teamList[i].imageName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); } if (uiInfo.teamList[i].cinematic >= 0) { trap_CIN_RunCinematic(uiInfo.teamList[i].cinematic); trap_CIN_SetExtents(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h); trap_CIN_DrawCinematic(uiInfo.teamList[i].cinematic); } else { trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal); trap_R_SetColor(NULL); uiInfo.teamList[i].cinematic = -2; } } else { trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon); trap_R_SetColor(NULL); } } } static void UI_DrawPreviewCinematic(rectDef_t *rect, float scale, vec4_t color) { if (uiInfo.previewMovie > -2) { uiInfo.previewMovie = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.movieList[uiInfo.movieIndex]), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); if (uiInfo.previewMovie >= 0) { trap_CIN_RunCinematic(uiInfo.previewMovie); trap_CIN_SetExtents(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h); trap_CIN_DrawCinematic(uiInfo.previewMovie); } else { uiInfo.previewMovie = -2; } } } static void UI_DrawSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i; i = trap_Cvar_VariableValue( "g_spSkill" ); if (i < 1 || i > numSkillLevels) { i = 1; } Text_Paint(rect->x, rect->y, scale, color, skillLevels[i-1],0, 0, textStyle); } static void UI_DrawTeamName(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int textStyle) { int i; i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam")); if (i >= 0 && i < uiInfo.teamCount) { Text_Paint(rect->x, rect->y, scale, color, va("%s: %s", (blue) ? "Blue" : "Red", uiInfo.teamList[i].teamName),0, 0, textStyle); } } static void UI_DrawTeamMember(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int num, int textStyle) { // 0 - None // 1 - Human // 2..NumCharacters - Bot int value = trap_Cvar_VariableValue(va(blue ? "ui_blueteam%i" : "ui_redteam%i", num)); const char *text; if (value <= 0) { text = "Closed"; } else if (value == 1) { text = "Human"; } else { value -= 2; if (ui_actualNetGameType.integer >= GT_TEAM) { if (value >= uiInfo.characterCount) { value = 0; } text = uiInfo.characterList[value].name; } else { if (value >= UI_GetNumBots()) { value = 0; } text = UI_GetBotNameByNumber(value); } } Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle); } static void UI_DrawEffects(rectDef_t *rect, float scale, vec4_t color) { UI_DrawHandlePic( rect->x, rect->y - 14, 128, 8, uiInfo.uiDC.Assets.fxBasePic ); UI_DrawHandlePic( rect->x + uiInfo.effectsColor * 16 + 8, rect->y - 16, 16, 12, uiInfo.uiDC.Assets.fxPic[uiInfo.effectsColor] ); } static void UI_DrawMapPreview(rectDef_t *rect, float scale, vec4_t color, qboolean net) { int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; if (map < 0 || map > uiInfo.mapCount) { if (net) { ui_currentNetMap.integer = 0; trap_Cvar_Set("ui_currentNetMap", "0"); } else { ui_currentMap.integer = 0; trap_Cvar_Set("ui_currentMap", "0"); } map = 0; } if (uiInfo.mapList[map].levelShot == -1) { uiInfo.mapList[map].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[map].imageName); } if (uiInfo.mapList[map].levelShot > 0) { UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.mapList[map].levelShot); } else { UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("menu/art/unknownmap")); } } static void UI_DrawMapTimeToBeat(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int minutes, seconds, time; if (ui_currentMap.integer < 0 || ui_currentMap.integer > uiInfo.mapCount) { ui_currentMap.integer = 0; trap_Cvar_Set("ui_currentMap", "0"); } time = uiInfo.mapList[ui_currentMap.integer].timeToBeat[uiInfo.gameTypes[ui_gameType.integer].gtEnum]; minutes = time / 60; seconds = time % 60; Text_Paint(rect->x, rect->y, scale, color, va("%02i:%02i", minutes, seconds), 0, 0, textStyle); } static void UI_DrawMapCinematic(rectDef_t *rect, float scale, vec4_t color, qboolean net) { int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; if (map < 0 || map > uiInfo.mapCount) { if (net) { ui_currentNetMap.integer = 0; trap_Cvar_Set("ui_currentNetMap", "0"); } else { ui_currentMap.integer = 0; trap_Cvar_Set("ui_currentMap", "0"); } map = 0; } if (uiInfo.mapList[map].cinematic >= -1) { if (uiInfo.mapList[map].cinematic == -1) { uiInfo.mapList[map].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[map].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); } if (uiInfo.mapList[map].cinematic >= 0) { trap_CIN_RunCinematic(uiInfo.mapList[map].cinematic); trap_CIN_SetExtents(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h); trap_CIN_DrawCinematic(uiInfo.mapList[map].cinematic); } else { uiInfo.mapList[map].cinematic = -2; } } else { UI_DrawMapPreview(rect, scale, color, net); } } static qboolean updateModel = qtrue; static qboolean q3Model = qfalse; static void UI_DrawPlayerModel(rectDef_t *rect) { static playerInfo_t info; char model[MAX_QPATH]; char team[256]; char head[256]; vec3_t viewangles; vec3_t moveangles; if (trap_Cvar_VariableValue("ui_Q3Model")) { strcpy(model, UI_Cvar_VariableString("model")); strcpy(head, UI_Cvar_VariableString("headmodel")); if (!q3Model) { q3Model = qtrue; updateModel = qtrue; } team[0] = '\0'; } else { strcpy(team, UI_Cvar_VariableString("ui_teamName")); strcpy(model, UI_Cvar_VariableString("team_model")); strcpy(head, UI_Cvar_VariableString("team_headmodel")); if (q3Model) { q3Model = qfalse; updateModel = qtrue; } } if (updateModel) { memset( &info, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 10; viewangles[PITCH] = 0; viewangles[ROLL] = 0; VectorClear( moveangles ); UI_PlayerInfo_SetModel( &info, model, head, team); UI_PlayerInfo_SetInfo( &info, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); // UI_RegisterClientModelname( &info, model, head, team); updateModel = qfalse; } UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info, uiInfo.uiDC.realTime / 2); } static void UI_DrawPlayerModel2(rectDef_t *rect) { static playerInfo_t info; char model[MAX_QPATH]; char team[256]; char head[256]; vec3_t viewangles; vec3_t moveangles; if (trap_Cvar_VariableValue("ui_Q3Model")) { strcpy(model, UI_Cvar_VariableString("model")); strcpy(head, UI_Cvar_VariableString("headmodel")); if (!q3Model) { q3Model = qtrue; updateModel = qtrue; } team[0] = '\0'; } else { strcpy(team, UI_Cvar_VariableString("ui_teamName")); strcpy(model, UI_Cvar_VariableString("team_model")); strcpy(head, UI_Cvar_VariableString("team_headmodel")); if (q3Model) { q3Model = qfalse; updateModel = qtrue; } } if (updateModel) { memset( &info, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 10; viewangles[PITCH] = 0; viewangles[ROLL] = 0; VectorClear( moveangles ); UI_PlayerInfo_SetModel( &info, model, head, team); UI_PlayerInfo_SetInfo( &info, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); // UI_RegisterClientModelname( &info, model, head, team); updateModel = qfalse; } UI_DrawPlayerII( rect->x, rect->y, rect->w, rect->h, &info, uiInfo.uiDC.realTime / 2); } static void UI_DrawNetSource(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (ui_netSource.integer < 0 || ui_netSource.integer > numNetSources) { ui_netSource.integer = 0; } Text_Paint(rect->x, rect->y, scale, color, va("Source: %s", netSources[ui_netSource.integer]), 0, 0, textStyle); } static void UI_DrawNetMapPreview(rectDef_t *rect, float scale, vec4_t color) { if (uiInfo.serverStatus.currentServerPreview > 0) { UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.serverStatus.currentServerPreview); } else { UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("menu/art/unknownmap")); } } static void UI_DrawNetMapCinematic(rectDef_t *rect, float scale, vec4_t color) { if (ui_currentNetMap.integer < 0 || ui_currentNetMap.integer > uiInfo.mapCount) { ui_currentNetMap.integer = 0; trap_Cvar_Set("ui_currentNetMap", "0"); } if (uiInfo.serverStatus.currentServerCinematic >= 0) { trap_CIN_RunCinematic(uiInfo.serverStatus.currentServerCinematic); trap_CIN_SetExtents(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h); trap_CIN_DrawCinematic(uiInfo.serverStatus.currentServerCinematic); } else { UI_DrawNetMapPreview(rect, scale, color); } } static void UI_DrawNetFilter(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { ui_serverFilterType.integer = 0; } Text_Paint(rect->x, rect->y, scale, color, va("Filter: %s", serverFilters[ui_serverFilterType.integer].description), 0, 0, textStyle); } static void UI_DrawTier(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i; i = trap_Cvar_VariableValue( "ui_currentTier" ); if (i < 0 || i >= uiInfo.tierCount) { i = 0; } Text_Paint(rect->x, rect->y, scale, color, va("Tier: %s", uiInfo.tierList[i].tierName),0, 0, textStyle); } static void UI_DrawTierMap(rectDef_t *rect, int index) { int i; i = trap_Cvar_VariableValue( "ui_currentTier" ); if (i < 0 || i >= uiInfo.tierCount) { i = 0; } if (uiInfo.tierList[i].mapHandles[index] == -1) { uiInfo.tierList[i].mapHandles[index] = trap_R_RegisterShaderNoMip(va("levelshots/%s", uiInfo.tierList[i].maps[index])); } UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.tierList[i].mapHandles[index]); } static const char *UI_EnglishMapName(const char *map) { int i; for (i = 0; i < uiInfo.mapCount; i++) { if (Q_stricmp(map, uiInfo.mapList[i].mapLoadName) == 0) { return uiInfo.mapList[i].mapName; } } return ""; } static void UI_DrawTierMapName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i, j; i = trap_Cvar_VariableValue( "ui_currentTier" ); if (i < 0 || i >= uiInfo.tierCount) { i = 0; } j = trap_Cvar_VariableValue("ui_currentMap"); if (j < 0 || j > MAPS_PER_TIER) { j = 0; } Text_Paint(rect->x, rect->y, scale, color, UI_EnglishMapName(uiInfo.tierList[i].maps[j]), 0, 0, textStyle); } static void UI_DrawTierGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i, j; i = trap_Cvar_VariableValue( "ui_currentTier" ); if (i < 0 || i >= uiInfo.tierCount) { i = 0; } j = trap_Cvar_VariableValue("ui_currentMap"); if (j < 0 || j > MAPS_PER_TIER) { j = 0; } Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[uiInfo.tierList[i].gameTypes[j]].gameType , 0, 0, textStyle); } #ifndef MISSIONPACK // bk001206 static const char *UI_OpponentLeaderName(void) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); return uiInfo.teamList[i].teamMembers[0]; } #endif static const char *UI_AIFromName(const char *name) { int j; for (j = 0; j < uiInfo.aliasCount; j++) { if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) { return uiInfo.aliasList[j].ai; } } return "sergei"; } #ifndef MISSIONPACK // bk001206 static const int UI_AIIndex(const char *name) { int j; for (j = 0; j < uiInfo.characterCount; j++) { if (Q_stricmp(name, uiInfo.characterList[j].name) == 0) { return j; } } return 0; } #endif #ifndef MISSIONPACK // bk001206 static const int UI_AIIndexFromName(const char *name) { int j; for (j = 0; j < uiInfo.aliasCount; j++) { if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) { return UI_AIIndex(uiInfo.aliasList[j].ai); } } return 0; } #endif #ifndef MISSIONPACK // bk001206 static const char *UI_OpponentLeaderHead(void) { const char *leader = UI_OpponentLeaderName(); return UI_AIFromName(leader); } #endif #ifndef MISSIONPACK // bk001206 static const char *UI_OpponentLeaderModel(void) { int i; const char *head = UI_OpponentLeaderHead(); for (i = 0; i < uiInfo.characterCount; i++) { if (Q_stricmp(head, uiInfo.characterList[i].name) == 0) { return uiInfo.characterList[i].base; } } return "sergei"; } #endif static qboolean updateOpponentModel = qtrue; static void UI_DrawOpponent(rectDef_t *rect) { static playerInfo_t info2; char model[MAX_QPATH]; char headmodel[MAX_QPATH]; char team[256]; vec3_t viewangles; vec3_t moveangles; if (updateOpponentModel) { strcpy(model, UI_Cvar_VariableString("ui_opponentModel")); strcpy(headmodel, UI_Cvar_VariableString("ui_opponentModel")); team[0] = '\0'; memset( &info2, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 10; viewangles[PITCH] = 0; viewangles[ROLL] = 0; VectorClear( moveangles ); UI_PlayerInfo_SetModel( &info2, model, headmodel, ""); UI_PlayerInfo_SetInfo( &info2, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); UI_RegisterClientModelname( &info2, model, headmodel, team); updateOpponentModel = qfalse; } UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info2, uiInfo.uiDC.realTime / 2); } static void UI_DrawOpponent2(rectDef_t *rect) { static playerInfo_t info2; char model[MAX_QPATH]; char headmodel[MAX_QPATH]; char team[256]; vec3_t viewangles; vec3_t moveangles; if (updateOpponentModel) { strcpy(model, UI_Cvar_VariableString("ui_opponentModel")); strcpy(headmodel, UI_Cvar_VariableString("ui_opponentModel")); team[0] = '\0'; memset( &info2, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 10; viewangles[PITCH] = 0; viewangles[ROLL] = 0; VectorClear( moveangles ); UI_PlayerInfo_SetModel( &info2, model, headmodel, ""); UI_PlayerInfo_SetInfo( &info2, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); UI_RegisterClientModelname( &info2, model, headmodel, team); updateOpponentModel = qfalse; } UI_DrawPlayerII( rect->x, rect->y, rect->w, rect->h, &info2, uiInfo.uiDC.realTime / 2); } static void UI_NextOpponent( void ) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); i++; if (i >= uiInfo.teamCount) { i = 0; } if (i == j) { i++; if ( i >= uiInfo.teamCount) { i = 0; } } trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName ); } static void UI_PriorOpponent( void ) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); i--; if (i < 0) { i = uiInfo.teamCount - 1; } if (i == j) { i--; if ( i < 0) { i = uiInfo.teamCount - 1; } } trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName ); } static void UI_DrawPlayerLogo(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon ); trap_R_SetColor( NULL ); } static void UI_DrawPlayerLogoMetal(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal ); trap_R_SetColor( NULL ); } static void UI_DrawPlayerLogoName(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name ); trap_R_SetColor( NULL ); } static void UI_DrawOpponentLogo(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon ); trap_R_SetColor( NULL ); } static void UI_DrawOpponentLogoMetal(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal ); trap_R_SetColor( NULL ); } static void UI_DrawOpponentLogoName(rectDef_t *rect, vec3_t color) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); if (uiInfo.teamList[i].teamIcon == -1) { uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); } trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name ); trap_R_SetColor( NULL ); } static void UI_DrawAllMapsSelection(rectDef_t *rect, float scale, vec4_t color, int textStyle, qboolean net) { int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; if (map >= 0 && map < uiInfo.mapCount) { Text_Paint(rect->x, rect->y, scale, color, uiInfo.mapList[map].mapName, 0, 0, textStyle); } } static void UI_DrawOpponentName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle); } static int UI_OwnerDrawWidth(int ownerDraw, float scale) { int i, h, value; const char *text; const char *s = NULL; switch (ownerDraw) { case UI_HANDICAP: h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); i = 20 - h / 5; s = handicapValues[i]; break; case UI_CLANNAME: s = UI_Cvar_VariableString("ui_teamName"); break; case UI_GAMETYPE: s = uiInfo.gameTypes[ui_gameType.integer].gameType; break; case UI_SKILL: i = trap_Cvar_VariableValue( "g_spSkill" ); if (i < 1 || i > numSkillLevels) { i = 1; } s = skillLevels[i-1]; break; case UI_BLUETEAMNAME: i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_blueTeam")); if (i >= 0 && i < uiInfo.teamCount) { s = va("%s: %s", "Blue", uiInfo.teamList[i].teamName); } break; case UI_REDTEAMNAME: i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_redTeam")); if (i >= 0 && i < uiInfo.teamCount) { s = va("%s: %s", "Red", uiInfo.teamList[i].teamName); } break; case UI_BLUETEAM1: case UI_BLUETEAM2: case UI_BLUETEAM3: case UI_BLUETEAM4: case UI_BLUETEAM5: value = trap_Cvar_VariableValue(va("ui_blueteam%i", ownerDraw-UI_BLUETEAM1 + 1)); if (value <= 0) { text = "Closed"; } else if (value == 1) { text = "Human"; } else { value -= 2; if (value >= uiInfo.aliasCount) { value = 0; } text = uiInfo.aliasList[value].name; } s = va("%i. %s", ownerDraw-UI_BLUETEAM1 + 1, text); break; case UI_REDTEAM1: case UI_REDTEAM2: case UI_REDTEAM3: case UI_REDTEAM4: case UI_REDTEAM5: value = trap_Cvar_VariableValue(va("ui_redteam%i", ownerDraw-UI_REDTEAM1 + 1)); if (value <= 0) { text = "Closed"; } else if (value == 1) { text = "Human"; } else { value -= 2; if (value >= uiInfo.aliasCount) { value = 0; } text = uiInfo.aliasList[value].name; } s = va("%i. %s", ownerDraw-UI_REDTEAM1 + 1, text); break; case UI_NETSOURCE: if (ui_netSource.integer < 0 || ui_netSource.integer > uiInfo.numJoinGameTypes) { ui_netSource.integer = 0; } s = va("Source: %s", netSources[ui_netSource.integer]); break; case UI_NETFILTER: if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { ui_serverFilterType.integer = 0; } s = va("Filter: %s", serverFilters[ui_serverFilterType.integer].description ); break; case UI_TIER: break; case UI_TIER_MAPNAME: break; case UI_TIER_GAMETYPE: break; case UI_ALLMAPS_SELECTION: break; case UI_OPPONENT_NAME: break; case UI_KEYBINDSTATUS: if (Display_KeyBindPending()) { s = "Waiting for new key... Press ESCAPE to cancel"; } else { s = "Press ENTER or CLICK to change, Press BACKSPACE to clear"; } break; case UI_SERVERREFRESHDATE: s = UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)); break; default: break; } if (s) { return Text_Width(s, scale, 0); } return 0; } static void UI_DrawBotName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int value = uiInfo.botIndex; int game = trap_Cvar_VariableValue("g_gametype"); const char *text = ""; if (game >= GT_TEAM && !GT_LMS ) { if (value >= uiInfo.characterCount) { value = 0; } text = uiInfo.characterList[value].name; } else { if (value >= UI_GetNumBots()) { value = 0; } text = UI_GetBotNameByNumber(value); } Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle); } static void UI_DrawBotSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (uiInfo.skillIndex >= 0 && uiInfo.skillIndex < numSkillLevels) { Text_Paint(rect->x, rect->y, scale, color, skillLevels[uiInfo.skillIndex], 0, 0, textStyle); } } static void UI_DrawRedBlue(rectDef_t *rect, float scale, vec4_t color, int textStyle) { Text_Paint(rect->x, rect->y, scale, color, (uiInfo.redBlue == 0) ? "Red" : "Blue", 0, 0, textStyle); } static void UI_DrawCrosshair(rectDef_t *rect, float scale, vec4_t color) { trap_R_SetColor( color ); if (uiInfo.currentCrosshair < 0 || uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { uiInfo.currentCrosshair = 0; } UI_DrawHandlePic( rect->x, rect->y - rect->h, rect->w, rect->h, uiInfo.uiDC.Assets.crosshairShader[uiInfo.currentCrosshair]); trap_R_SetColor( NULL ); } /* =============== UI_BuildPlayerList =============== */ static void UI_BuildPlayerList( void ) { uiClientState_t cs; int n, count, team, team2, playerTeamNumber; char info[MAX_INFO_STRING]; trap_GetClientState( &cs ); trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING ); uiInfo.playerNumber = cs.clientNum; uiInfo.teamLeader = atoi(Info_ValueForKey(info, "tl")); team = atoi(Info_ValueForKey(info, "t")); trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); count = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); uiInfo.playerCount = 0; uiInfo.myTeamCount = 0; playerTeamNumber = 0; for( n = 0; n < count; n++ ) { trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); if (info[0]) { Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] ); uiInfo.playerCount++; team2 = atoi(Info_ValueForKey(info, "t")); if (team2 == team) { Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); Q_CleanStr( uiInfo.teamNames[uiInfo.myTeamCount] ); uiInfo.teamClientNums[uiInfo.myTeamCount] = n; if (uiInfo.playerNumber == n) { playerTeamNumber = uiInfo.myTeamCount; } uiInfo.myTeamCount++; } } } if (!uiInfo.teamLeader) { trap_Cvar_Set("cg_selectedPlayer", va("%d", playerTeamNumber)); } n = trap_Cvar_VariableValue("cg_selectedPlayer"); if (n < 0 || n > uiInfo.myTeamCount) { n = 0; } if (n < uiInfo.myTeamCount) { trap_Cvar_Set("cg_selectedPlayerName", uiInfo.teamNames[n]); } } static void UI_DrawSelectedPlayer(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; UI_BuildPlayerList(); } Text_Paint(rect->x, rect->y, scale, color, (uiInfo.teamLeader) ? UI_Cvar_VariableString("cg_selectedPlayerName") : UI_Cvar_VariableString("name") , 0, 0, textStyle); } static void UI_DrawServerRefreshDate(rectDef_t *rect, float scale, vec4_t color, int textStyle) { if (uiInfo.serverStatus.refreshActive) { vec4_t lowLight, newColor; lowLight[0] = 0.8 * color[0]; lowLight[1] = 0.8 * color[1]; lowLight[2] = 0.8 * color[2]; lowLight[3] = 0.8 * color[3]; LerpColor(color,lowLight,newColor,0.5+0.5*sin(uiInfo.uiDC.realTime / PULSE_DIVISOR)); Text_Paint(rect->x, rect->y, scale, newColor, va("Getting info for %d servers (ESC to cancel)", trap_LAN_GetServerCount(ui_netSource.integer)), 0, 0, textStyle); } else { char buff[64]; Q_strncpyz(buff, UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)), 64); Text_Paint(rect->x, rect->y, scale, color, va("Refresh Time: %s", buff), 0, 0, textStyle); } } static void UI_DrawServerMOTD(rectDef_t *rect, float scale, vec4_t color) { if (uiInfo.serverStatus.motdLen) { float maxX; if (uiInfo.serverStatus.motdWidth == -1) { uiInfo.serverStatus.motdWidth = 0; uiInfo.serverStatus.motdPaintX = rect->x + 1; uiInfo.serverStatus.motdPaintX2 = -1; } if (uiInfo.serverStatus.motdOffset > uiInfo.serverStatus.motdLen) { uiInfo.serverStatus.motdOffset = 0; uiInfo.serverStatus.motdPaintX = rect->x + 1; uiInfo.serverStatus.motdPaintX2 = -1; } if (uiInfo.uiDC.realTime > uiInfo.serverStatus.motdTime) { uiInfo.serverStatus.motdTime = uiInfo.uiDC.realTime + 10; if (uiInfo.serverStatus.motdPaintX <= rect->x + 2) { if (uiInfo.serverStatus.motdOffset < uiInfo.serverStatus.motdLen) { uiInfo.serverStatus.motdPaintX += Text_Width(&uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], scale, 1) - 1; uiInfo.serverStatus.motdOffset++; } else { uiInfo.serverStatus.motdOffset = 0; if (uiInfo.serverStatus.motdPaintX2 >= 0) { uiInfo.serverStatus.motdPaintX = uiInfo.serverStatus.motdPaintX2; } else { uiInfo.serverStatus.motdPaintX = rect->x + rect->w - 2; } uiInfo.serverStatus.motdPaintX2 = -1; } } else { //serverStatus.motdPaintX--; uiInfo.serverStatus.motdPaintX -= 2; if (uiInfo.serverStatus.motdPaintX2 >= 0) { //serverStatus.motdPaintX2--; uiInfo.serverStatus.motdPaintX2 -= 2; } } } maxX = rect->x + rect->w - 2; Text_Paint_Limit(&maxX, uiInfo.serverStatus.motdPaintX, rect->y + rect->h - 3, scale, color, &uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], 0, 0); if (uiInfo.serverStatus.motdPaintX2 >= 0) { float maxX2 = rect->x + rect->w - 2; Text_Paint_Limit(&maxX2, uiInfo.serverStatus.motdPaintX2, rect->y + rect->h - 3, scale, color, uiInfo.serverStatus.motd, 0, uiInfo.serverStatus.motdOffset); } if (uiInfo.serverStatus.motdOffset && maxX > 0) { // if we have an offset ( we are skipping the first part of the string ) and we fit the string if (uiInfo.serverStatus.motdPaintX2 == -1) { uiInfo.serverStatus.motdPaintX2 = rect->x + rect->w - 2; } } else { uiInfo.serverStatus.motdPaintX2 = -1; } } } static void UI_DrawKeyBindStatus(rectDef_t *rect, float scale, vec4_t color, int textStyle) { // int ofs = 0; TTimo: unused if (Display_KeyBindPending()) { Text_Paint(rect->x, rect->y, scale, color, "Waiting for new key... Press ESCAPE to cancel", 0, 0, textStyle); } else { Text_Paint(rect->x, rect->y, scale, color, "Press ENTER or CLICK to change, Press BACKSPACE to clear", 0, 0, textStyle); } } static void UI_DrawGLInfo(rectDef_t *rect, float scale, vec4_t color, int textStyle) { char * eptr; char buff[1024]; const char *lines[64]; int y, numLines, i; Text_Paint(rect->x + 2, rect->y, scale, color, va("VENDOR: %s", uiInfo.uiDC.glconfig.vendor_string), 0, 30, textStyle); Text_Paint(rect->x + 2, rect->y + 15, scale, color, va("VERSION: %s: %s", uiInfo.uiDC.glconfig.version_string,uiInfo.uiDC.glconfig.renderer_string), 0, 30, textStyle); Text_Paint(rect->x + 2, rect->y + 30, scale, color, va ("PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)", uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits, uiInfo.uiDC.glconfig.stencilBits), 0, 30, textStyle); // build null terminated extension strings // TTimo: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399 // in TA this was not directly crashing, but displaying a nasty broken shader right in the middle // brought down the string size to 1024, there's not much that can be shown on the screen anyway Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 1024); eptr = buff; y = rect->y + 45; numLines = 0; while ( y < rect->y + rect->h && *eptr ) { while ( *eptr && *eptr == ' ' ) *eptr++ = '\0'; // track start of valid string if (*eptr && *eptr != ' ') { lines[numLines++] = eptr; } while ( *eptr && *eptr != ' ' ) eptr++; } i = 0; while (i < numLines) { Text_Paint(rect->x + 2, y, scale, color, lines[i++], 0, 20, textStyle); if (i < numLines) { Text_Paint(rect->x + rect->w / 2, y, scale, color, lines[i++], 0, 20, textStyle); } y += 10; if (y > rect->y + rect->h - 11) { break; } } } // FIXME: table drive // static void UI_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle) { rectDef_t rect; rect.x = x + text_x; rect.y = y + text_y; rect.w = w; rect.h = h; switch (ownerDraw) { case UI_HANDICAP: UI_DrawHandicap(&rect, scale, color, textStyle); break; case UI_EFFECTS: UI_DrawEffects(&rect, scale, color); break; case UI_PLAYERMODEL: UI_DrawPlayerModel(&rect); break; case UI_PLAYERMODEL2: UI_DrawPlayerModel2(&rect); break; case UI_CLANNAME: UI_DrawClanName(&rect, scale, color, textStyle); break; case UI_CLANLOGO: UI_DrawClanLogo(&rect, scale, color); break; case UI_CLANCINEMATIC: UI_DrawClanCinematic(&rect, scale, color); break; case UI_PREVIEWCINEMATIC: UI_DrawPreviewCinematic(&rect, scale, color); break; case UI_GAMETYPE: UI_DrawGameType(&rect, scale, color, textStyle); break; case UI_NETGAMETYPE: UI_DrawNetGameType(&rect, scale, color, textStyle); break; case UI_JOINGAMETYPE: UI_DrawJoinGameType(&rect, scale, color, textStyle); break; case UI_MAPPREVIEW: UI_DrawMapPreview(&rect, scale, color, qtrue); break; case UI_MAP_TIMETOBEAT: UI_DrawMapTimeToBeat(&rect, scale, color, textStyle); break; case UI_MAPCINEMATIC: UI_DrawMapCinematic(&rect, scale, color, qfalse); break; case UI_STARTMAPCINEMATIC: UI_DrawMapCinematic(&rect, scale, color, qtrue); break; case UI_SKILL: UI_DrawSkill(&rect, scale, color, textStyle); break; case UI_BLUETEAMNAME: UI_DrawTeamName(&rect, scale, color, qtrue, textStyle); break; case UI_REDTEAMNAME: UI_DrawTeamName(&rect, scale, color, qfalse, textStyle); break; case UI_BLUETEAM1: case UI_BLUETEAM2: case UI_BLUETEAM3: case UI_BLUETEAM4: case UI_BLUETEAM5: UI_DrawTeamMember(&rect, scale, color, qtrue, ownerDraw - UI_BLUETEAM1 + 1, textStyle); break; case UI_REDTEAM1: case UI_REDTEAM2: case UI_REDTEAM3: case UI_REDTEAM4: case UI_REDTEAM5: UI_DrawTeamMember(&rect, scale, color, qfalse, ownerDraw - UI_REDTEAM1 + 1, textStyle); break; case UI_NETSOURCE: UI_DrawNetSource(&rect, scale, color, textStyle); break; case UI_NETMAPPREVIEW: UI_DrawNetMapPreview(&rect, scale, color); break; case UI_NETMAPCINEMATIC: UI_DrawNetMapCinematic(&rect, scale, color); break; case UI_NETFILTER: UI_DrawNetFilter(&rect, scale, color, textStyle); break; case UI_TIER: UI_DrawTier(&rect, scale, color, textStyle); break; case UI_OPPONENTMODEL: UI_DrawOpponent(&rect); break; case UI_OPPONENTMODEL2: UI_DrawOpponent2(&rect); break; case UI_TIERMAP1: UI_DrawTierMap(&rect, 0); break; case UI_TIERMAP2: UI_DrawTierMap(&rect, 1); break; case UI_TIERMAP3: UI_DrawTierMap(&rect, 2); break; case UI_PLAYERLOGO: UI_DrawPlayerLogo(&rect, color); break; case UI_PLAYERLOGO_METAL: UI_DrawPlayerLogoMetal(&rect, color); break; case UI_PLAYERLOGO_NAME: UI_DrawPlayerLogoName(&rect, color); break; case UI_OPPONENTLOGO: UI_DrawOpponentLogo(&rect, color); break; case UI_OPPONENTLOGO_METAL: UI_DrawOpponentLogoMetal(&rect, color); break; case UI_OPPONENTLOGO_NAME: UI_DrawOpponentLogoName(&rect, color); break; case UI_TIER_MAPNAME: UI_DrawTierMapName(&rect, scale, color, textStyle); break; case UI_TIER_GAMETYPE: UI_DrawTierGameType(&rect, scale, color, textStyle); break; case UI_ALLMAPS_SELECTION: UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qtrue); break; case UI_MAPS_SELECTION: UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qfalse); break; case UI_OPPONENT_NAME: UI_DrawOpponentName(&rect, scale, color, textStyle); break; case UI_BOTNAME: UI_DrawBotName(&rect, scale, color, textStyle); break; case UI_BOTSKILL: UI_DrawBotSkill(&rect, scale, color, textStyle); break; case UI_REDBLUE: UI_DrawRedBlue(&rect, scale, color, textStyle); break; case UI_CROSSHAIR: UI_DrawCrosshair(&rect, scale, color); break; case UI_SELECTEDPLAYER: UI_DrawSelectedPlayer(&rect, scale, color, textStyle); break; case UI_SERVERREFRESHDATE: UI_DrawServerRefreshDate(&rect, scale, color, textStyle); break; case UI_SERVERMOTD: UI_DrawServerMOTD(&rect, scale, color); break; case UI_GLINFO: UI_DrawGLInfo(&rect,scale, color, textStyle); break; case UI_KEYBINDSTATUS: UI_DrawKeyBindStatus(&rect,scale, color, textStyle); break; default: break; } } static qboolean UI_OwnerDrawVisible(int flags) { qboolean vis = qtrue; while (flags) { if (flags & UI_SHOW_FFA) { if (trap_Cvar_VariableValue("g_gametype") != GT_FFA || trap_Cvar_VariableValue("g_gametype") != GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_FFA; } if (flags & UI_SHOW_NOTFFA) { if (trap_Cvar_VariableValue("g_gametype") == GT_FFA || trap_Cvar_VariableValue("g_gametype") == GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_NOTFFA; } if (flags & UI_SHOW_LEADER) { // these need to show when this client can give orders to a player or a group if (!uiInfo.teamLeader) { vis = qfalse; } else { // if showing yourself if (ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber) { vis = qfalse; } } flags &= ~UI_SHOW_LEADER; } if (flags & UI_SHOW_NOTLEADER) { // these need to show when this client is assigning their own status or they are NOT the leader if (uiInfo.teamLeader) { // if not showing yourself if (!(ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber)) { vis = qfalse; } // these need to show when this client can give orders to a player or a group } flags &= ~UI_SHOW_NOTLEADER; } if (flags & UI_SHOW_FAVORITESERVERS) { // this assumes you only put this type of display flag on something showing in the proper context if (ui_netSource.integer != AS_FAVORITES) { vis = qfalse; } flags &= ~UI_SHOW_FAVORITESERVERS; } if (flags & UI_SHOW_NOTFAVORITESERVERS) { // this assumes you only put this type of display flag on something showing in the proper context if (ui_netSource.integer == AS_FAVORITES) { vis = qfalse; } flags &= ~UI_SHOW_NOTFAVORITESERVERS; } if (flags & UI_SHOW_ANYTEAMGAME) { if (uiInfo.gameTypes[ui_gameType.integer].gtEnum <= GT_TEAM && uiInfo.gameTypes[ui_gameType.integer].gtEnum != GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_ANYTEAMGAME; } if (flags & UI_SHOW_ANYNONTEAMGAME) { if (uiInfo.gameTypes[ui_gameType.integer].gtEnum > GT_TEAM || uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_ANYNONTEAMGAME; } if (flags & UI_SHOW_NETANYTEAMGAME) { if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum <= GT_TEAM && uiInfo.gameTypes[ui_gameType.integer].gtEnum != GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_NETANYTEAMGAME; } if (flags & UI_SHOW_NETANYNONTEAMGAME) { if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum > GT_TEAM || uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_LMS ) { vis = qfalse; } flags &= ~UI_SHOW_NETANYNONTEAMGAME; } if (flags & UI_SHOW_NEWHIGHSCORE) { if (uiInfo.newHighScoreTime < uiInfo.uiDC.realTime) { vis = qfalse; } else { if (uiInfo.soundHighScore) { if (trap_Cvar_VariableValue("sv_killserver") == 0) { // wait on server to go down before playing sound trap_S_StartLocalSound(uiInfo.newHighScoreSound, CHAN_ANNOUNCER); uiInfo.soundHighScore = qfalse; } } } flags &= ~UI_SHOW_NEWHIGHSCORE; } if (flags & UI_SHOW_NEWBESTTIME) { if (uiInfo.newBestTime < uiInfo.uiDC.realTime) { vis = qfalse; } flags &= ~UI_SHOW_NEWBESTTIME; } if (flags & UI_SHOW_DEMOAVAILABLE) { if (!uiInfo.demoAvailable) { vis = qfalse; } flags &= ~UI_SHOW_DEMOAVAILABLE; } else { flags = 0; } } return vis; } static qboolean UI_Handicap_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int h; h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); if (key == K_MOUSE2) { h -= 5; } else { h += 5; } if (h > 100) { h = 5; } else if (h < 0) { h = 100; } trap_Cvar_Set( "handicap", va( "%i", h) ); return qtrue; } return qfalse; } static qboolean UI_Effects_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { uiInfo.effectsColor--; } else { uiInfo.effectsColor++; } if( uiInfo.effectsColor > 6 ) { uiInfo.effectsColor = 0; } else if (uiInfo.effectsColor < 0) { uiInfo.effectsColor = 6; } trap_Cvar_SetValue( "color1", uitogamecode[uiInfo.effectsColor] ); return qtrue; } return qfalse; } static qboolean UI_ClanName_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int i; i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (uiInfo.teamList[i].cinematic >= 0) { trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic); uiInfo.teamList[i].cinematic = -1; } if (key == K_MOUSE2) { i--; } else { i++; } if (i >= uiInfo.teamCount) { i = 0; } else if (i < 0) { i = uiInfo.teamCount - 1; } trap_Cvar_Set( "ui_teamName", uiInfo.teamList[i].teamName); UI_HeadCountByTeam(); UI_FeederSelection(FEEDER_HEADS, 0); updateModel = qtrue; return qtrue; } return qfalse; } static qboolean UI_GameType_HandleKey(int flags, float *special, int key, qboolean resetMap) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int oldCount = UI_MapCountByGameType(qtrue); // hard coded mess here if (key == K_MOUSE2) { ui_gameType.integer--; if (ui_gameType.integer == 2) { ui_gameType.integer = 1; } else if (ui_gameType.integer < 2) { ui_gameType.integer = uiInfo.numGameTypes - 1; } } else { ui_gameType.integer++; if (ui_gameType.integer >= uiInfo.numGameTypes) { ui_gameType.integer = 1; } else if (ui_gameType.integer == 2) { ui_gameType.integer = 3; } } if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_TOURNAMENT) { trap_Cvar_Set("ui_Q3Model", "1"); } else { trap_Cvar_Set("ui_Q3Model", "0"); } trap_Cvar_Set("ui_gameType", va("%d", ui_gameType.integer)); UI_SetCapFragLimits(qtrue); UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); if (resetMap && oldCount != UI_MapCountByGameType(qtrue)) { trap_Cvar_Set( "ui_currentMap", "0"); Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, NULL); } return qtrue; } return qfalse; } static qboolean UI_NetGameType_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { ui_netGameType.integer--; } else { ui_netGameType.integer++; } if (ui_netGameType.integer < 0) { ui_netGameType.integer = uiInfo.numGameTypes - 1; } else if (ui_netGameType.integer >= uiInfo.numGameTypes) { ui_netGameType.integer = 0; } trap_Cvar_Set( "ui_netGameType", va("%d", ui_netGameType.integer)); trap_Cvar_Set( "ui_actualnetGameType", va("%d", uiInfo.gameTypes[ui_netGameType.integer].gtEnum)); trap_Cvar_Set( "ui_currentNetMap", "0"); UI_MapCountByGameType(qfalse); Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, NULL); return qtrue; } return qfalse; } static qboolean UI_JoinGameType_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { ui_joinGameType.integer--; } else { ui_joinGameType.integer++; } if (ui_joinGameType.integer < 0) { ui_joinGameType.integer = uiInfo.numJoinGameTypes - 1; } else if (ui_joinGameType.integer >= uiInfo.numJoinGameTypes) { ui_joinGameType.integer = 0; } trap_Cvar_Set( "ui_joinGameType", va("%d", ui_joinGameType.integer)); UI_BuildServerDisplayList(qtrue); return qtrue; } return qfalse; } static qboolean UI_Skill_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int i = trap_Cvar_VariableValue( "g_spSkill" ); if (key == K_MOUSE2) { i--; } else { i++; } if (i < 1) { i = numSkillLevels; } else if (i > numSkillLevels) { i = 1; } trap_Cvar_Set("g_spSkill", va("%i", i)); return qtrue; } return qfalse; } static qboolean UI_TeamName_HandleKey(int flags, float *special, int key, qboolean blue) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int i; i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam")); if (key == K_MOUSE2) { i--; } else { i++; } if (i >= uiInfo.teamCount) { i = 0; } else if (i < 0) { i = uiInfo.teamCount - 1; } trap_Cvar_Set( (blue) ? "ui_blueTeam" : "ui_redTeam", uiInfo.teamList[i].teamName); return qtrue; } return qfalse; } static qboolean UI_TeamMember_HandleKey(int flags, float *special, int key, qboolean blue, int num) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { // 0 - None // 1 - Human // 2..NumCharacters - Bot char *cvar = va(blue ? "ui_blueteam%i" : "ui_redteam%i", num); int value = trap_Cvar_VariableValue(cvar); if (key == K_MOUSE2) { value--; } else { value++; } if (ui_actualNetGameType.integer >= GT_TEAM) { if (value >= uiInfo.characterCount + 2) { value = 0; } else if (value < 0) { value = uiInfo.characterCount + 2 - 1; } } else { if (value >= UI_GetNumBots() + 2) { value = 0; } else if (value < 0) { value = UI_GetNumBots() + 2 - 1; } } trap_Cvar_Set(cvar, va("%i", value)); return qtrue; } return qfalse; } static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { ui_netSource.integer--; if(strlen(netSources[ui_netSource.integer])<1) ui_netSource.integer--; } else { ui_netSource.integer++; if(strlen(netSources[ui_netSource.integer])<1) ui_netSource.integer++; } if (ui_netSource.integer >= numNetSources) { ui_netSource.integer = 0; } else if (ui_netSource.integer < 0) { ui_netSource.integer = numNetSources - 1; } UI_BuildServerDisplayList(qtrue); if (ui_netSource.integer != AS_GLOBAL) { UI_StartServerRefresh(qtrue); } trap_Cvar_Set( "ui_netSource", va("%d", ui_netSource.integer)); return qtrue; } return qfalse; } static qboolean UI_NetFilter_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { ui_serverFilterType.integer--; } else { ui_serverFilterType.integer++; } if (ui_serverFilterType.integer >= numServerFilters) { ui_serverFilterType.integer = 0; } else if (ui_serverFilterType.integer < 0) { ui_serverFilterType.integer = numServerFilters - 1; } UI_BuildServerDisplayList(qtrue); return qtrue; } return qfalse; } static qboolean UI_OpponentName_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { UI_PriorOpponent(); } else { UI_NextOpponent(); } return qtrue; } return qfalse; } static qboolean UI_BotName_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int game = trap_Cvar_VariableValue("g_gametype"); int value = uiInfo.botIndex; if (key == K_MOUSE2) { value--; } else { value++; } if (game >= GT_TEAM && !GT_LMS) { if (value >= uiInfo.characterCount + 2) { value = 0; } else if (value < 0) { value = uiInfo.characterCount + 2 - 1; } } else { if (value >= UI_GetNumBots() + 2) { value = 0; } else if (value < 0) { value = UI_GetNumBots() + 2 - 1; } } uiInfo.botIndex = value; return qtrue; } return qfalse; } static qboolean UI_BotSkill_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { uiInfo.skillIndex--; } else { uiInfo.skillIndex++; } if (uiInfo.skillIndex >= numSkillLevels) { uiInfo.skillIndex = 0; } else if (uiInfo.skillIndex < 0) { uiInfo.skillIndex = numSkillLevels-1; } return qtrue; } return qfalse; } static qboolean UI_RedBlue_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { uiInfo.redBlue ^= 1; return qtrue; } return qfalse; } static qboolean UI_Crosshair_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { if (key == K_MOUSE2) { uiInfo.currentCrosshair--; } else { uiInfo.currentCrosshair++; } if (uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { uiInfo.currentCrosshair = 0; } else if (uiInfo.currentCrosshair < 0) { uiInfo.currentCrosshair = NUM_CROSSHAIRS - 1; } trap_Cvar_Set("cg_drawCrosshair", va("%d", uiInfo.currentCrosshair)); return qtrue; } return qfalse; } static qboolean UI_SelectedPlayer_HandleKey(int flags, float *special, int key) { if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { int selected; UI_BuildPlayerList(); if (!uiInfo.teamLeader) { return qfalse; } selected = trap_Cvar_VariableValue("cg_selectedPlayer"); if (key == K_MOUSE2) { selected--; } else { selected++; } if (selected > uiInfo.myTeamCount) { selected = 0; } else if (selected < 0) { selected = uiInfo.myTeamCount; } if (selected == uiInfo.myTeamCount) { trap_Cvar_Set( "cg_selectedPlayerName", "Everyone"); } else { trap_Cvar_Set( "cg_selectedPlayerName", uiInfo.teamNames[selected]); } trap_Cvar_Set( "cg_selectedPlayer", va("%d", selected)); } return qfalse; } static qboolean UI_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key) { switch (ownerDraw) { case UI_HANDICAP: return UI_Handicap_HandleKey(flags, special, key); break; case UI_EFFECTS: return UI_Effects_HandleKey(flags, special, key); break; case UI_CLANNAME: return UI_ClanName_HandleKey(flags, special, key); break; case UI_GAMETYPE: return UI_GameType_HandleKey(flags, special, key, qtrue); break; case UI_NETGAMETYPE: return UI_NetGameType_HandleKey(flags, special, key); break; case UI_JOINGAMETYPE: return UI_JoinGameType_HandleKey(flags, special, key); break; case UI_SKILL: return UI_Skill_HandleKey(flags, special, key); break; case UI_BLUETEAMNAME: return UI_TeamName_HandleKey(flags, special, key, qtrue); break; case UI_REDTEAMNAME: return UI_TeamName_HandleKey(flags, special, key, qfalse); break; case UI_BLUETEAM1: case UI_BLUETEAM2: case UI_BLUETEAM3: case UI_BLUETEAM4: case UI_BLUETEAM5: UI_TeamMember_HandleKey(flags, special, key, qtrue, ownerDraw - UI_BLUETEAM1 + 1); break; case UI_REDTEAM1: case UI_REDTEAM2: case UI_REDTEAM3: case UI_REDTEAM4: case UI_REDTEAM5: UI_TeamMember_HandleKey(flags, special, key, qfalse, ownerDraw - UI_REDTEAM1 + 1); break; case UI_NETSOURCE: UI_NetSource_HandleKey(flags, special, key); break; case UI_NETFILTER: UI_NetFilter_HandleKey(flags, special, key); break; case UI_OPPONENT_NAME: UI_OpponentName_HandleKey(flags, special, key); break; case UI_BOTNAME: return UI_BotName_HandleKey(flags, special, key); break; case UI_BOTSKILL: return UI_BotSkill_HandleKey(flags, special, key); break; case UI_REDBLUE: UI_RedBlue_HandleKey(flags, special, key); break; case UI_CROSSHAIR: UI_Crosshair_HandleKey(flags, special, key); break; case UI_SELECTEDPLAYER: UI_SelectedPlayer_HandleKey(flags, special, key); break; default: break; } return qfalse; } static float UI_GetValue(int ownerDraw) { return 0; } /* ================= UI_ServersQsortCompare ================= */ static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 ) { return trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey, uiInfo.serverStatus.sortDir, *(int*)arg1, *(int*)arg2); } /* ================= UI_ServersSort ================= */ void UI_ServersSort(int column, qboolean force) { if ( !force ) { if ( uiInfo.serverStatus.sortKey == column ) { return; } } uiInfo.serverStatus.sortKey = column; qsort( &uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers, sizeof(int), UI_ServersQsortCompare); } /* static void UI_StartSinglePlayer(void) { int i,j, k, skill; char buff[1024]; i = trap_Cvar_VariableValue( "ui_currentTier" ); if (i < 0 || i >= tierCount) { i = 0; } j = trap_Cvar_VariableValue("ui_currentMap"); if (j < 0 || j > MAPS_PER_TIER) { j = 0; } trap_Cvar_SetValue( "singleplayer", 1 ); trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 7, tierList[i].gameTypes[j] ) ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", tierList[i].maps[j] ) ); skill = trap_Cvar_VariableValue( "g_spSkill" ); if (j == MAPS_PER_TIER-1) { k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[0]), skill, "", teamList[k].teamMembers[0]); } else { k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); for (i = 0; i < PLAYERS_PER_TEAM; i++) { Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Blue", teamList[k].teamMembers[i]); trap_Cmd_ExecuteText( EXEC_APPEND, buff ); } k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); for (i = 1; i < PLAYERS_PER_TEAM; i++) { Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Red", teamList[k].teamMembers[i]); trap_Cmd_ExecuteText( EXEC_APPEND, buff ); } trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); } } */ /* =============== UI_LoadMods =============== */ static void UI_LoadMods( void ) { int numdirs; char dirlist[2048]; char *dirptr; char *descptr; int i; int dirlen; uiInfo.modCount = 0; numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) ); dirptr = dirlist; for( i = 0; i < numdirs; i++ ) { dirlen = strlen( dirptr ) + 1; descptr = dirptr + dirlen; uiInfo.modList[uiInfo.modCount].modName = String_Alloc(dirptr); uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc(descptr); dirptr += dirlen + strlen(descptr) + 1; uiInfo.modCount++; if (uiInfo.modCount >= MAX_MODS) { break; } } } /* =============== UI_LoadTeams =============== */ static void UI_LoadTeams( void ) { char teamList[4096]; char *teamName; int i, len, count; count = trap_FS_GetFileList( "", "team", teamList, 4096 ); if (count) { teamName = teamList; for ( i = 0; i < count; i++ ) { len = strlen( teamName ); UI_ParseTeamInfo(teamName); teamName += len + 1; } } } /* =============== UI_LoadMovies =============== */ static void UI_LoadMovies( void ) { char movielist[4096]; char *moviename; int i, len; uiInfo.movieCount = trap_FS_GetFileList( "video", "roq", movielist, 4096 ); if (uiInfo.movieCount) { if (uiInfo.movieCount > MAX_MOVIES) { uiInfo.movieCount = MAX_MOVIES; } moviename = movielist; for ( i = 0; i < uiInfo.movieCount; i++ ) { len = strlen( moviename ); if (!Q_stricmp(moviename + len - 4,".roq")) { moviename[len-4] = '\0'; } Q_strupr(moviename); uiInfo.movieList[i] = String_Alloc(moviename); moviename += len + 1; } } } /* =============== UI_LoadDemos =============== */ static void UI_LoadDemos( void ) { char demolist[4096]; char demoExt[32]; char *demoname; int i, len; Com_sprintf(demoExt, sizeof(demoExt), "dm_%d", (int)trap_Cvar_VariableValue("protocol")); uiInfo.demoCount = trap_FS_GetFileList( "demos", demoExt, demolist, 4096 ); Com_sprintf(demoExt, sizeof(demoExt), ".dm_%d", (int)trap_Cvar_VariableValue("protocol")); if (uiInfo.demoCount) { if (uiInfo.demoCount > MAX_DEMOS) { uiInfo.demoCount = MAX_DEMOS; } demoname = demolist; for ( i = 0; i < uiInfo.demoCount; i++ ) { len = strlen( demoname ); if (!Q_stricmp(demoname + len - strlen(demoExt), demoExt)) { demoname[len-strlen(demoExt)] = '\0'; } Q_strupr(demoname); uiInfo.demoList[i] = String_Alloc(demoname); demoname += len + 1; } } } static qboolean UI_SetNextMap(int actual, int index) { int i; for (i = actual + 1; i < uiInfo.mapCount; i++) { if (uiInfo.mapList[i].active) { Menu_SetFeederSelection(NULL, FEEDER_MAPS, index + 1, "skirmish"); return qtrue; } } return qfalse; } static void UI_StartSkirmish(qboolean next) { int i, k, g, delay, temp; float skill; char buff[MAX_STRING_CHARS]; if (next) { int actual; int index = trap_Cvar_VariableValue("ui_mapIndex"); UI_MapCountByGameType(qtrue); UI_SelectedMap(index, &actual); if (UI_SetNextMap(actual, index)) { } else { UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse); UI_MapCountByGameType(qtrue); Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, "skirmish"); } } g = uiInfo.gameTypes[ui_gameType.integer].gtEnum; trap_Cvar_SetValue( "g_gametype", g ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName) ); skill = trap_Cvar_VariableValue( "g_spSkill" ); trap_Cvar_Set("ui_scoreMap", uiInfo.mapList[ui_currentMap.integer].mapName); k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); trap_Cvar_Set("ui_singlePlayerActive", "1"); // set up sp overrides, will be replaced on postgame temp = trap_Cvar_VariableValue( "capturelimit" ); trap_Cvar_Set("ui_saveCaptureLimit", va("%i", temp)); temp = trap_Cvar_VariableValue( "fraglimit" ); trap_Cvar_Set("ui_saveFragLimit", va("%i", temp)); UI_SetCapFragLimits(qfalse); temp = trap_Cvar_VariableValue( "cg_drawTimer" ); trap_Cvar_Set("ui_drawTimer", va("%i", temp)); temp = trap_Cvar_VariableValue( "g_doWarmup" ); trap_Cvar_Set("ui_doWarmup", va("%i", temp)); temp = trap_Cvar_VariableValue( "g_friendlyFire" ); trap_Cvar_Set("ui_friendlyFire", va("%i", temp)); temp = trap_Cvar_VariableValue( "sv_maxClients" ); trap_Cvar_Set("ui_maxClients", va("%i", temp)); temp = trap_Cvar_VariableValue( "g_warmup" ); trap_Cvar_Set("ui_Warmup", va("%i", temp)); temp = trap_Cvar_VariableValue( "sv_pure" ); trap_Cvar_Set("ui_pure", va("%i", temp)); trap_Cvar_Set("cg_cameraOrbit", "0"); trap_Cvar_Set("cg_thirdPerson", "0"); trap_Cvar_Set("cg_drawTimer", "1"); trap_Cvar_Set("g_doWarmup", "1"); trap_Cvar_Set("g_warmup", "15"); trap_Cvar_Set("sv_pure", "0"); trap_Cvar_Set("g_friendlyFire", "0"); trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName")); trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName")); if (trap_Cvar_VariableValue("ui_recordSPDemo")) { Com_sprintf(buff, MAX_STRING_CHARS, "%s_%i", uiInfo.mapList[ui_currentMap.integer].mapLoadName, g); trap_Cvar_Set("ui_recordSPDemoName", buff); } delay = 500; if (g == GT_TOURNAMENT) { trap_Cvar_Set("sv_maxClients", "2"); Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %f "", %i \n", uiInfo.mapList[ui_currentMap.integer].opponentName, skill, delay); trap_Cmd_ExecuteText( EXEC_APPEND, buff ); } else { temp = uiInfo.mapList[ui_currentMap.integer].teamMembers * 2; trap_Cvar_Set("sv_maxClients", va("%d", temp)); for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers; i++) { Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, (g == GT_FFA) ? "" : "Blue", delay, uiInfo.teamList[k].teamMembers[i]); trap_Cmd_ExecuteText( EXEC_APPEND, buff ); delay += 500; } k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers-1; i++) { Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, (g == GT_FFA) ? "" : "Red", delay, uiInfo.teamList[k].teamMembers[i]); trap_Cmd_ExecuteText( EXEC_APPEND, buff ); delay += 500; } } if (g >= GT_TEAM ) { trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); } } static void UI_Update(const char *name) { int val = trap_Cvar_VariableValue(name); if (Q_stricmp(name, "ui_SetName") == 0) { trap_Cvar_Set( "name", UI_Cvar_VariableString("ui_Name")); } else if (Q_stricmp(name, "ui_setRate") == 0) { float rate = trap_Cvar_VariableValue("rate"); if (rate >= 5000) { trap_Cvar_Set("cl_maxpackets", "30"); trap_Cvar_Set("cl_packetdup", "1"); } else if (rate >= 4000) { trap_Cvar_Set("cl_maxpackets", "15"); trap_Cvar_Set("cl_packetdup", "2"); // favor less prediction errors when there's packet loss } else { trap_Cvar_Set("cl_maxpackets", "15"); trap_Cvar_Set("cl_packetdup", "1"); // favor lower bandwidth } } else if (Q_stricmp(name, "ui_GetName") == 0) { trap_Cvar_Set( "ui_Name", UI_Cvar_VariableString("name")); } else if (Q_stricmp(name, "r_colorbits") == 0) { switch (val) { case 0: trap_Cvar_SetValue( "r_depthbits", 0 ); trap_Cvar_SetValue( "r_stencilbits", 0 ); break; case 16: trap_Cvar_SetValue( "r_depthbits", 16 ); trap_Cvar_SetValue( "r_stencilbits", 0 ); break; case 32: trap_Cvar_SetValue( "r_depthbits", 24 ); break; } } else if (Q_stricmp(name, "r_lodbias") == 0) { switch (val) { case 0: trap_Cvar_SetValue( "r_subdivisions", 4 ); break; case 1: trap_Cvar_SetValue( "r_subdivisions", 12 ); break; case 2: trap_Cvar_SetValue( "r_subdivisions", 20 ); break; } } else if (Q_stricmp(name, "ui_glCustom") == 0) { switch (val) { case 0: // high quality trap_Cvar_SetValue( "r_fullScreen", 1 ); trap_Cvar_SetValue( "r_subdivisions", 4 ); trap_Cvar_SetValue( "r_vertexlight", 0 ); trap_Cvar_SetValue( "r_lodbias", 0 ); trap_Cvar_SetValue( "r_colorbits", 32 ); trap_Cvar_SetValue( "r_depthbits", 24 ); trap_Cvar_SetValue( "r_picmip", 0 ); trap_Cvar_SetValue( "r_mode", 4 ); trap_Cvar_SetValue( "r_texturebits", 32 ); trap_Cvar_SetValue( "r_fastSky", 0 ); trap_Cvar_SetValue( "r_inGameVideo", 1 ); trap_Cvar_SetValue( "cg_shadows", 1 ); trap_Cvar_SetValue( "cg_brassTime", 2500 ); trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); break; case 1: // normal trap_Cvar_SetValue( "r_fullScreen", 1 ); trap_Cvar_SetValue( "r_subdivisions", 12 ); trap_Cvar_SetValue( "r_vertexlight", 0 ); trap_Cvar_SetValue( "r_lodbias", 0 ); trap_Cvar_SetValue( "r_colorbits", 0 ); trap_Cvar_SetValue( "r_depthbits", 24 ); trap_Cvar_SetValue( "r_picmip", 1 ); trap_Cvar_SetValue( "r_mode", 3 ); trap_Cvar_SetValue( "r_texturebits", 0 ); trap_Cvar_SetValue( "r_fastSky", 0 ); trap_Cvar_SetValue( "r_inGameVideo", 1 ); trap_Cvar_SetValue( "cg_brassTime", 2500 ); trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); trap_Cvar_SetValue( "cg_shadows", 0 ); break; case 2: // fast trap_Cvar_SetValue( "r_fullScreen", 1 ); trap_Cvar_SetValue( "r_subdivisions", 8 ); trap_Cvar_SetValue( "r_vertexlight", 0 ); trap_Cvar_SetValue( "r_lodbias", 1 ); trap_Cvar_SetValue( "r_colorbits", 0 ); trap_Cvar_SetValue( "r_depthbits", 0 ); trap_Cvar_SetValue( "r_picmip", 1 ); trap_Cvar_SetValue( "r_mode", 3 ); trap_Cvar_SetValue( "r_texturebits", 0 ); trap_Cvar_SetValue( "cg_shadows", 0 ); trap_Cvar_SetValue( "r_fastSky", 1 ); trap_Cvar_SetValue( "r_inGameVideo", 0 ); trap_Cvar_SetValue( "cg_brassTime", 0 ); trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); break; case 3: // fastest trap_Cvar_SetValue( "r_fullScreen", 1 ); trap_Cvar_SetValue( "r_subdivisions", 20 ); trap_Cvar_SetValue( "r_vertexlight", 1 ); trap_Cvar_SetValue( "r_lodbias", 2 ); trap_Cvar_SetValue( "r_colorbits", 16 ); trap_Cvar_SetValue( "r_depthbits", 16 ); trap_Cvar_SetValue( "r_mode", 3 ); trap_Cvar_SetValue( "r_picmip", 2 ); trap_Cvar_SetValue( "r_texturebits", 16 ); trap_Cvar_SetValue( "cg_shadows", 0 ); trap_Cvar_SetValue( "cg_brassTime", 0 ); trap_Cvar_SetValue( "r_fastSky", 1 ); trap_Cvar_SetValue( "r_inGameVideo", 0 ); trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); break; } } else if (Q_stricmp(name, "ui_mousePitch") == 0) { if (val == 0) { trap_Cvar_SetValue( "m_pitch", 0.022f ); } else { trap_Cvar_SetValue( "m_pitch", -0.022f ); } } } static void UI_RunMenuScript(char **args) { const char *name, *name2; char buff[1024]; if (String_Parse(args, &name)) { if (Q_stricmp(name, "StartServer") == 0) { int i, clients, oldclients; float skill; trap_Cvar_Set("cg_thirdPerson", "0"); trap_Cvar_Set("cg_cameraOrbit", "0"); trap_Cvar_Set("ui_singlePlayerActive", "0"); trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, ui_dedicated.integer ) ); trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 13, uiInfo.gameTypes[ui_netGameType.integer].gtEnum ) ); trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName")); trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName")); trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName ) ); skill = trap_Cvar_VariableValue( "g_spSkill" ); // set max clients based on spots oldclients = trap_Cvar_VariableValue( "sv_maxClients" ); clients = 0; for (i = 0; i < PLAYERS_PER_TEAM; i++) { int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1)); if (bot >= 0) { clients++; } bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1)); if (bot >= 0) { clients++; } } if (clients == 0) { clients = 8; } if (oldclients > clients) { clients = oldclients; } trap_Cvar_Set("sv_maxClients", va("%d",clients)); for (i = 0; i < PLAYERS_PER_TEAM; i++) { int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1)); if (bot > 1) { if (ui_actualNetGameType.integer >= GT_TEAM) { Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Blue"); } else { Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill); } trap_Cmd_ExecuteText( EXEC_APPEND, buff ); } bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1)); if (bot > 1) { if (ui_actualNetGameType.integer >= GT_TEAM) { Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Red"); } else { Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill); } trap_Cmd_ExecuteText( EXEC_APPEND, buff ); } } } else if (Q_stricmp(name, "updateSPMenu") == 0) { UI_SetCapFragLimits(qtrue); UI_MapCountByGameType(qtrue); ui_mapIndex.integer = UI_GetIndexFromSelection(ui_currentMap.integer); trap_Cvar_Set("ui_mapIndex", va("%d", ui_mapIndex.integer)); Menu_SetFeederSelection(NULL, FEEDER_MAPS, ui_mapIndex.integer, "skirmish"); UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse); UI_GameType_HandleKey(0, NULL, K_MOUSE2, qfalse); } else if (Q_stricmp(name, "resetDefaults") == 0) { trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n"); trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n"); Controls_SetDefaults(); trap_Cvar_Set("com_introPlayed", "1" ); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" ); } else if (Q_stricmp(name, "getCDKey") == 0) { char out[17]; trap_GetCDKey(buff, 17); trap_Cvar_Set("cdkey1", ""); trap_Cvar_Set("cdkey2", ""); trap_Cvar_Set("cdkey3", ""); trap_Cvar_Set("cdkey4", ""); if (strlen(buff) == CDKEY_LEN) { Q_strncpyz(out, buff, 5); trap_Cvar_Set("cdkey1", out); Q_strncpyz(out, buff + 4, 5); trap_Cvar_Set("cdkey2", out); Q_strncpyz(out, buff + 8, 5); trap_Cvar_Set("cdkey3", out); Q_strncpyz(out, buff + 12, 5); trap_Cvar_Set("cdkey4", out); } } else if (Q_stricmp(name, "verifyCDKey") == 0) { buff[0] = '\0'; Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey1")); Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey2")); Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey3")); Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey4")); trap_Cvar_Set("cdkey", buff); if (trap_VerifyCDKey(buff, UI_Cvar_VariableString("cdkeychecksum"))) { trap_Cvar_Set("ui_cdkeyvalid", "CD Key Appears to be valid."); trap_SetCDKey(buff); } else { trap_Cvar_Set("ui_cdkeyvalid", "CD Key does not appear to be valid."); } } else if (Q_stricmp(name, "loadArenas") == 0) { UI_LoadArenas(); UI_MapCountByGameType(qfalse); Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver"); } else if (Q_stricmp(name, "saveControls") == 0) { Controls_SetConfig(qtrue); } else if (Q_stricmp(name, "loadControls") == 0) { Controls_GetConfig(); } else if (Q_stricmp(name, "clearError") == 0) { trap_Cvar_Set("com_errorMessage", ""); } else if (Q_stricmp(name, "loadGameInfo") == 0) { #ifdef PRE_RELEASE_TADEMO UI_ParseGameInfo("demogameinfo.txt"); #else UI_ParseGameInfo("gameinfo.txt"); #endif UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); } else if (Q_stricmp(name, "resetScores") == 0) { UI_ClearScores(); } else if (Q_stricmp(name, "RefreshServers") == 0) { UI_StartServerRefresh(qtrue); UI_BuildServerDisplayList(qtrue); } else if (Q_stricmp(name, "RefreshFilter") == 0) { UI_StartServerRefresh(qfalse); UI_BuildServerDisplayList(qtrue); } else if (Q_stricmp(name, "RunSPDemo") == 0) { if (uiInfo.demoAvailable) { trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s_%i\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum)); } } else if (Q_stricmp(name, "LoadDemos") == 0) { UI_LoadDemos(); } else if (Q_stricmp(name, "LoadMovies") == 0) { UI_LoadMovies(); } else if (Q_stricmp(name, "LoadMods") == 0) { UI_LoadMods(); } else if (Q_stricmp(name, "playMovie") == 0) { if (uiInfo.previewMovie >= 0) { trap_CIN_StopCinematic(uiInfo.previewMovie); } trap_Cmd_ExecuteText( EXEC_APPEND, va("cinematic %s.roq 2\n", uiInfo.movieList[uiInfo.movieIndex])); } else if (Q_stricmp(name, "RunMod") == 0) { trap_Cvar_Set( "fs_game", uiInfo.modList[uiInfo.modIndex].modName); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); } else if (Q_stricmp(name, "RunDemo") == 0) { trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s\n", uiInfo.demoList[uiInfo.demoIndex])); } else if (Q_stricmp(name, "Quake3") == 0) { trap_Cvar_Set( "fs_game", ""); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); } else if (Q_stricmp(name, "closeJoin") == 0) { if (uiInfo.serverStatus.refreshActive) { UI_StopServerRefresh(); uiInfo.serverStatus.nextDisplayRefresh = 0; uiInfo.nextServerStatusRefresh = 0; uiInfo.nextFindPlayerRefresh = 0; UI_BuildServerDisplayList(qtrue); } else { Menus_CloseByName("joinserver"); Menus_OpenByName("main"); } } else if (Q_stricmp(name, "StopRefresh") == 0) { UI_StopServerRefresh(); uiInfo.serverStatus.nextDisplayRefresh = 0; uiInfo.nextServerStatusRefresh = 0; uiInfo.nextFindPlayerRefresh = 0; } else if (Q_stricmp(name, "UpdateFilter") == 0) { if (ui_netSource.integer == AS_LOCAL) { UI_StartServerRefresh(qtrue); } UI_BuildServerDisplayList(qtrue); UI_FeederSelection(FEEDER_SERVERS, 0); } else if (Q_stricmp(name, "ServerStatus") == 0) { trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], uiInfo.serverStatusAddress, sizeof(uiInfo.serverStatusAddress)); UI_BuildServerStatus(qtrue); } else if (Q_stricmp(name, "FoundPlayerServerStatus") == 0) { Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress)); UI_BuildServerStatus(qtrue); Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL); } else if (Q_stricmp(name, "FindPlayer") == 0) { UI_BuildFindPlayerList(qtrue); // clear the displayed server status info uiInfo.serverStatusInfo.numLines = 0; Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL); } else if (Q_stricmp(name, "JoinServer") == 0) { trap_Cvar_Set("cg_thirdPerson", "0"); trap_Cvar_Set("cg_cameraOrbit", "0"); trap_Cvar_Set("ui_singlePlayerActive", "0"); if (uiInfo.serverStatus.currentServer >= 0 && uiInfo.serverStatus.currentServer < uiInfo.serverStatus.numDisplayServers) { trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, 1024); trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) ); } } else if (Q_stricmp(name, "FoundPlayerJoinServer") == 0) { trap_Cvar_Set("ui_singlePlayerActive", "0"); if (uiInfo.currentFoundPlayerServer >= 0 && uiInfo.currentFoundPlayerServer < uiInfo.numFoundPlayerServers) { trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer] ) ); } } else if (Q_stricmp(name, "Quit") == 0) { trap_Cvar_Set("ui_singlePlayerActive", "0"); trap_Cmd_ExecuteText( EXEC_NOW, "quit"); } else if (Q_stricmp(name, "Controls") == 0) { trap_Cvar_Set( "cl_paused", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); Menus_CloseAll(); Menus_ActivateByName("setup_menu2"); } else if (Q_stricmp(name, "Leave") == 0) { trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" ); trap_Key_SetCatcher( KEYCATCH_UI ); Menus_CloseAll(); Menus_ActivateByName("main"); } else if (Q_stricmp(name, "ServerSort") == 0) { int sortColumn; if (Int_Parse(args, &sortColumn)) { // if same column we're already sorting on then flip the direction if (sortColumn == uiInfo.serverStatus.sortKey) { uiInfo.serverStatus.sortDir = !uiInfo.serverStatus.sortDir; } // make sure we sort again UI_ServersSort(sortColumn, qtrue); } } else if (Q_stricmp(name, "nextSkirmish") == 0) { UI_StartSkirmish(qtrue); } else if (Q_stricmp(name, "SkirmishStart") == 0) { UI_StartSkirmish(qfalse); } else if (Q_stricmp(name, "closeingame") == 0) { trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); Menus_CloseAll(); } else if (Q_stricmp(name, "voteMap") == 0) { if (ui_currentNetMap.integer >=0 && ui_currentNetMap.integer < uiInfo.mapCount) { trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote map %s\n",uiInfo.mapList[ui_currentNetMap.integer].mapLoadName) ); } } else if (Q_stricmp(name, "voteKick") == 0) { if (uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount) { trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote kick %s\n",uiInfo.playerNames[uiInfo.playerIndex]) ); } } else if (Q_stricmp(name, "voteGame") == 0) { if (ui_netGameType.integer >= 0 && ui_netGameType.integer < uiInfo.numGameTypes) { trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote g_gametype %i\n",uiInfo.gameTypes[ui_netGameType.integer].gtEnum) ); } } else if (Q_stricmp(name, "voteLeader") == 0) { if (uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount) { trap_Cmd_ExecuteText( EXEC_APPEND, va("callteamvote leader %s\n",uiInfo.teamNames[uiInfo.teamIndex]) ); } } else if (Q_stricmp(name, "addBot") == 0) { if (trap_Cvar_VariableValue("g_gametype") >= GT_TEAM && !GT_LMS ) { trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", uiInfo.characterList[uiInfo.botIndex].name, uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); } else { trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", UI_GetBotNameByNumber(uiInfo.botIndex), uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); } } else if (Q_stricmp(name, "addFavorite") == 0) { if (ui_netSource.integer != AS_FAVORITES) { char name[MAX_NAME_LENGTH]; char addr[MAX_NAME_LENGTH]; int res; trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS); name[0] = addr[0] = '\0'; Q_strncpyz(name, Info_ValueForKey(buff, "hostname"), MAX_NAME_LENGTH); Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH); if (strlen(name) > 0 && strlen(addr) > 0) { res = trap_LAN_AddServer(AS_FAVORITES, name, addr); if (res == 0) { // server already in the list Com_Printf("Favorite already in list\n"); } else if (res == -1) { // list full Com_Printf("Favorite list full\n"); } else { // successfully added Com_Printf("Added favorite server %s\n", addr); } } } } else if (Q_stricmp(name, "deleteFavorite") == 0) { if (ui_netSource.integer == AS_FAVORITES) { char addr[MAX_NAME_LENGTH]; trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS); addr[0] = '\0'; Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH); if (strlen(addr) > 0) { trap_LAN_RemoveServer(AS_FAVORITES, addr); } } } else if (Q_stricmp(name, "createFavorite") == 0) { if (ui_netSource.integer == AS_FAVORITES) { char name[MAX_NAME_LENGTH]; char addr[MAX_NAME_LENGTH]; int res; name[0] = addr[0] = '\0'; Q_strncpyz(name, UI_Cvar_VariableString("ui_favoriteName"), MAX_NAME_LENGTH); Q_strncpyz(addr, UI_Cvar_VariableString("ui_favoriteAddress"), MAX_NAME_LENGTH); if (strlen(name) > 0 && strlen(addr) > 0) { res = trap_LAN_AddServer(AS_FAVORITES, name, addr); if (res == 0) { // server already in the list Com_Printf("Favorite already in list\n"); } else if (res == -1) { // list full Com_Printf("Favorite list full\n"); } else { // successfully added Com_Printf("Added favorite server %s\n", addr); } } } } else if (Q_stricmp(name, "orders") == 0) { const char *orders; if (String_Parse(args, &orders)) { int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); if (selectedPlayer < uiInfo.myTeamCount) { strcpy(buff, orders); trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) ); trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); } else { int i; for (i = 0; i < uiInfo.myTeamCount; i++) { if (Q_stricmp(UI_Cvar_VariableString("name"), uiInfo.teamNames[i]) == 0) { continue; } strcpy(buff, orders); trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamNames[i]) ); trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); } } trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); Menus_CloseAll(); } } else if (Q_stricmp(name, "voiceOrdersTeam") == 0) { const char *orders; if (String_Parse(args, &orders)) { int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); if (selectedPlayer == uiInfo.myTeamCount) { trap_Cmd_ExecuteText( EXEC_APPEND, orders ); trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); } trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); Menus_CloseAll(); } } else if (Q_stricmp(name, "voiceOrders") == 0) { const char *orders; if (String_Parse(args, &orders)) { int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); if (selectedPlayer < uiInfo.myTeamCount) { strcpy(buff, orders); trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) ); trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); } trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); Menus_CloseAll(); } } else if (Q_stricmp(name, "glCustom") == 0) { trap_Cvar_Set("ui_glCustom", "4"); } else if (Q_stricmp(name, "update") == 0) { if (String_Parse(args, &name2)) { UI_Update(name2); } } else if (Q_stricmp(name, "setPbClStatus") == 0) { int stat; if ( Int_Parse( args, &stat ) ) trap_SetPbClStatus( stat ); } else { Com_Printf("unknown UI script %s\n", name); } } } static void UI_GetTeamColor(vec4_t *color) { } /* ================== UI_MapCountByGameType ================== */ static int UI_MapCountByGameType(qboolean singlePlayer) { int i, c, game; c = 0; game = singlePlayer ? uiInfo.gameTypes[ui_gameType.integer].gtEnum : uiInfo.gameTypes[ui_netGameType.integer].gtEnum; if (game == GT_SINGLE_PLAYER) { game++; } if (game == GT_TEAM) { game = GT_FFA; } for (i = 0; i < uiInfo.mapCount; i++) { uiInfo.mapList[i].active = qfalse; if ( uiInfo.mapList[i].typeBits & (1 << game)) { if (singlePlayer) { if (!(uiInfo.mapList[i].typeBits & (1 << GT_SINGLE_PLAYER))) { continue; } } c++; uiInfo.mapList[i].active = qtrue; } } return c; } qboolean UI_hasSkinForBase(const char *base, const char *team) { char test[1024]; Com_sprintf( test, sizeof( test ), "models/players/%s/%s/lower_default.skin", base, team ); if (trap_FS_FOpenFile(test, NULL, FS_READ)) { return qtrue; } Com_sprintf( test, sizeof( test ), "models/players/characters/%s/%s/lower_default.skin", base, team ); if (trap_FS_FOpenFile(test, NULL, FS_READ)) { return qtrue; } return qfalse; } /* ================== UI_MapCountByTeam ================== */ static int UI_HeadCountByTeam(void) { static int init = 0; int i, j, k, c, tIndex; c = 0; if (!init) { for (i = 0; i < uiInfo.characterCount; i++) { uiInfo.characterList[i].reference = 0; for (j = 0; j < uiInfo.teamCount; j++) { if (UI_hasSkinForBase(uiInfo.characterList[i].base, uiInfo.teamList[j].teamName)) { uiInfo.characterList[i].reference |= (1< uiInfo.serverStatus.numDisplayServers ) { return; } // uiInfo.serverStatus.numDisplayServers++; for (i = uiInfo.serverStatus.numDisplayServers; i > position; i--) { uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i-1]; } uiInfo.serverStatus.displayServers[position] = num; } /* ================== UI_RemoveServerFromDisplayList ================== */ static void UI_RemoveServerFromDisplayList(int num) { int i, j; for (i = 0; i < uiInfo.serverStatus.numDisplayServers; i++) { if (uiInfo.serverStatus.displayServers[i] == num) { uiInfo.serverStatus.numDisplayServers--; for (j = i; j < uiInfo.serverStatus.numDisplayServers; j++) { uiInfo.serverStatus.displayServers[j] = uiInfo.serverStatus.displayServers[j+1]; } return; } } } /* ================== UI_BinaryServerInsertion ================== */ static void UI_BinaryServerInsertion(int num) { int mid, offset, res, len; // use binary search to insert server len = uiInfo.serverStatus.numDisplayServers; mid = len; offset = 0; res = 0; while(mid > 0) { mid = len >> 1; // res = trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey, uiInfo.serverStatus.sortDir, num, uiInfo.serverStatus.displayServers[offset+mid]); // if equal if (res == 0) { UI_InsertServerIntoDisplayList(num, offset+mid); return; } // if larger else if (res == 1) { offset += mid; len -= mid; } // if smaller else { len -= mid; } } if (res == 1) { offset++; } UI_InsertServerIntoDisplayList(num, offset); } /* ================== UI_BuildServerDisplayList ================== */ static void UI_BuildServerDisplayList(qboolean force) { int i, count, clients, maxClients, ping, game, len, visible; char info[MAX_STRING_CHARS]; // qboolean startRefresh = qtrue; TTimo: unused static int numinvisible; if (!(force || uiInfo.uiDC.realTime > uiInfo.serverStatus.nextDisplayRefresh)) { return; } // if we shouldn't reset if ( force == 2 ) { force = 0; } // do motd updates here too trap_Cvar_VariableStringBuffer( "cl_motdString", uiInfo.serverStatus.motd, sizeof(uiInfo.serverStatus.motd) ); len = strlen(uiInfo.serverStatus.motd); if (len == 0) { strcpy(uiInfo.serverStatus.motd, "Welcome to Team Arena!"); len = strlen(uiInfo.serverStatus.motd); } if (len != uiInfo.serverStatus.motdLen) { uiInfo.serverStatus.motdLen = len; uiInfo.serverStatus.motdWidth = -1; } if (force) { numinvisible = 0; // clear number of displayed servers uiInfo.serverStatus.numDisplayServers = 0; uiInfo.serverStatus.numPlayersOnServers = 0; // set list box index to zero Menu_SetFeederSelection(NULL, FEEDER_SERVERS, 0, NULL); // mark all servers as visible so we store ping updates for them trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue); } // get the server count (comes from the master) count = trap_LAN_GetServerCount(ui_netSource.integer); if (count == -1 || (ui_netSource.integer == AS_LOCAL && count == 0) ) { // still waiting on a response from the master uiInfo.serverStatus.numDisplayServers = 0; uiInfo.serverStatus.numPlayersOnServers = 0; uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 500; return; } visible = qfalse; for (i = 0; i < count; i++) { // if we already got info for this server if (!trap_LAN_ServerIsVisible(ui_netSource.integer, i)) { continue; } visible = qtrue; // get the ping for this server ping = trap_LAN_GetServerPing(ui_netSource.integer, i); if (ping > 0 || ui_netSource.integer == AS_FAVORITES) { trap_LAN_GetServerInfo(ui_netSource.integer, i, info, MAX_STRING_CHARS); if(trap_Cvar_VariableValue("ui_humansonly")) clients = atoi(Info_ValueForKey(info, "g_humanplayers")); else clients = atoi(Info_ValueForKey(info, "clients")); uiInfo.serverStatus.numPlayersOnServers += clients; if (ui_browserShowEmpty.integer == 0) { if (clients == 0) { trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); continue; } } if (ui_browserShowFull.integer == 0) { maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); if (clients == maxClients) { trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); continue; } } if (uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum != -1) { game = atoi(Info_ValueForKey(info, "gametype")); if (game != uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum) { trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); continue; } } if (ui_serverFilterType.integer > 0) { if (Q_stricmp(Info_ValueForKey(info, "game"), serverFilters[ui_serverFilterType.integer].basedir) != 0) { trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); continue; } } // make sure we never add a favorite server twice if (ui_netSource.integer == AS_FAVORITES) { UI_RemoveServerFromDisplayList(i); } // insert the server into the list UI_BinaryServerInsertion(i); // done with this server if (ping > 0) { trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); numinvisible++; } } } uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime; // if there were no servers visible for ping updates if (!visible) { // UI_StopServerRefresh(); // uiInfo.serverStatus.nextDisplayRefresh = 0; } } typedef struct { char *name, *altName; } serverStatusCvar_t; serverStatusCvar_t serverStatusCvars[] = { {"sv_hostname", "Name"}, {"Address", ""}, {"gamename", "Game name"}, {"g_gametype", "Game type"}, {"mapname", "Map"}, {"version", ""}, {"protocol", ""}, {"timelimit", ""}, {"fraglimit", ""}, {NULL, NULL} }; /* ================== UI_SortServerStatusInfo ================== */ static void UI_SortServerStatusInfo( serverStatusInfo_t *info ) { int i, j, index; char *tmp1, *tmp2; // FIXME: if "gamename" == "baseq3" or "missionpack" then // replace the gametype number by FFA, CTF etc. // index = 0; for (i = 0; serverStatusCvars[i].name; i++) { for (j = 0; j < info->numLines; j++) { if ( !info->lines[j][1] || info->lines[j][1][0] ) { continue; } if ( !Q_stricmp(serverStatusCvars[i].name, info->lines[j][0]) ) { // swap lines tmp1 = info->lines[index][0]; tmp2 = info->lines[index][3]; info->lines[index][0] = info->lines[j][0]; info->lines[index][3] = info->lines[j][3]; info->lines[j][0] = tmp1; info->lines[j][3] = tmp2; // if ( strlen(serverStatusCvars[i].altName) ) { info->lines[index][0] = serverStatusCvars[i].altName; } index++; } } } } /* ================== UI_GetServerStatusInfo ================== */ static int UI_GetServerStatusInfo( const char *serverAddress, serverStatusInfo_t *info ) { char *p, *score, *ping, *name; int i, len; if (!info) { trap_LAN_ServerStatus( serverAddress, NULL, 0); return qfalse; } memset(info, 0, sizeof(*info)); if ( trap_LAN_ServerStatus( serverAddress, info->text, sizeof(info->text)) ) { Q_strncpyz(info->address, serverAddress, sizeof(info->address)); p = info->text; info->numLines = 0; info->lines[info->numLines][0] = "Address"; info->lines[info->numLines][1] = ""; info->lines[info->numLines][2] = ""; info->lines[info->numLines][3] = info->address; info->numLines++; // get the cvars while (p && *p) { p = strchr(p, '\\'); if (!p) break; *p++ = '\0'; if (*p == '\\') break; info->lines[info->numLines][0] = p; info->lines[info->numLines][1] = ""; info->lines[info->numLines][2] = ""; p = strchr(p, '\\'); if (!p) break; *p++ = '\0'; info->lines[info->numLines][3] = p; info->numLines++; if (info->numLines >= MAX_SERVERSTATUS_LINES) break; } // get the player list if (info->numLines < MAX_SERVERSTATUS_LINES-3) { // empty line info->lines[info->numLines][0] = ""; info->lines[info->numLines][1] = ""; info->lines[info->numLines][2] = ""; info->lines[info->numLines][3] = ""; info->numLines++; // header info->lines[info->numLines][0] = "num"; info->lines[info->numLines][1] = "score"; info->lines[info->numLines][2] = "ping"; info->lines[info->numLines][3] = "name"; info->numLines++; // parse players i = 0; len = 0; while (p && *p) { if (*p == '\\') *p++ = '\0'; if (!p) break; score = p; p = strchr(p, ' '); if (!p) break; *p++ = '\0'; ping = p; p = strchr(p, ' '); if (!p) break; *p++ = '\0'; name = p; Com_sprintf(&info->pings[len], sizeof(info->pings)-len, "%d", i); info->lines[info->numLines][0] = &info->pings[len]; len += strlen(&info->pings[len]) + 1; info->lines[info->numLines][1] = score; info->lines[info->numLines][2] = ping; info->lines[info->numLines][3] = name; info->numLines++; if (info->numLines >= MAX_SERVERSTATUS_LINES) break; p = strchr(p, '\\'); if (!p) break; *p++ = '\0'; // i++; } } UI_SortServerStatusInfo( info ); return qtrue; } return qfalse; } /* ================== stristr ================== */ static char *stristr(char *str, char *charset) { int i; while(*str) { for (i = 0; charset[i] && str[i]; i++) { if (toupper(charset[i]) != toupper(str[i])) break; } if (!charset[i]) return str; str++; } return NULL; } /* ================== UI_BuildFindPlayerList ================== */ static void UI_BuildFindPlayerList(qboolean force) { static int numFound, numTimeOuts; int i, j, resend; serverStatusInfo_t info; char name[MAX_NAME_LENGTH+2]; char infoString[MAX_STRING_CHARS]; if (!force) { if (!uiInfo.nextFindPlayerRefresh || uiInfo.nextFindPlayerRefresh > uiInfo.uiDC.realTime) { return; } } else { memset(&uiInfo.pendingServerStatus, 0, sizeof(uiInfo.pendingServerStatus)); uiInfo.numFoundPlayerServers = 0; uiInfo.currentFoundPlayerServer = 0; trap_Cvar_VariableStringBuffer( "ui_findPlayer", uiInfo.findPlayerName, sizeof(uiInfo.findPlayerName)); Q_CleanStr(uiInfo.findPlayerName); // should have a string of some length if (!strlen(uiInfo.findPlayerName)) { uiInfo.nextFindPlayerRefresh = 0; return; } // set resend time resend = ui_serverStatusTimeOut.integer / 2 - 10; if (resend < 50) { resend = 50; } trap_Cvar_Set("cl_serverStatusResendTime", va("%d", resend)); // reset all server status requests trap_LAN_ServerStatus( NULL, NULL, 0); // uiInfo.numFoundPlayerServers = 1; Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), "searching %d...", uiInfo.pendingServerStatus.num); numFound = 0; numTimeOuts++; } for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { // if this pending server is valid if (uiInfo.pendingServerStatus.server[i].valid) { // try to get the server status for this server if (UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, &info ) ) { // numFound++; // parse through the server status lines for (j = 0; j < info.numLines; j++) { // should have ping info if ( !info.lines[j][2] || !info.lines[j][2][0] ) { continue; } // clean string first Q_strncpyz(name, info.lines[j][3], sizeof(name)); Q_CleanStr(name); // if the player name is a substring if (stristr(name, uiInfo.findPlayerName)) { // add to found server list if we have space (always leave space for a line with the number found) if (uiInfo.numFoundPlayerServers < MAX_FOUNDPLAYER_SERVERS-1) { // Q_strncpyz(uiInfo.foundPlayerServerAddresses[uiInfo.numFoundPlayerServers-1], uiInfo.pendingServerStatus.server[i].adrstr, sizeof(uiInfo.foundPlayerServerAddresses[0])); Q_strncpyz(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], uiInfo.pendingServerStatus.server[i].name, sizeof(uiInfo.foundPlayerServerNames[0])); uiInfo.numFoundPlayerServers++; } else { // can't add any more so we're done uiInfo.pendingServerStatus.num = uiInfo.serverStatus.numDisplayServers; } } } Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound); // retrieved the server status so reuse this spot uiInfo.pendingServerStatus.server[i].valid = qfalse; } } // if empty pending slot or timed out if (!uiInfo.pendingServerStatus.server[i].valid || uiInfo.pendingServerStatus.server[i].startTime < uiInfo.uiDC.realTime - ui_serverStatusTimeOut.integer) { if (uiInfo.pendingServerStatus.server[i].valid) { numTimeOuts++; } // reset server status request for this address UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, NULL ); // reuse pending slot uiInfo.pendingServerStatus.server[i].valid = qfalse; // if we didn't try to get the status of all servers in the main browser yet if (uiInfo.pendingServerStatus.num < uiInfo.serverStatus.numDisplayServers) { uiInfo.pendingServerStatus.server[i].startTime = uiInfo.uiDC.realTime; trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num], uiInfo.pendingServerStatus.server[i].adrstr, sizeof(uiInfo.pendingServerStatus.server[i].adrstr)); trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num], infoString, sizeof(infoString)); Q_strncpyz(uiInfo.pendingServerStatus.server[i].name, Info_ValueForKey(infoString, "hostname"), sizeof(uiInfo.pendingServerStatus.server[0].name)); uiInfo.pendingServerStatus.server[i].valid = qtrue; uiInfo.pendingServerStatus.num++; Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound); } } } for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { if (uiInfo.pendingServerStatus.server[i].valid) { break; } } // if still trying to retrieve server status info if (i < MAX_SERVERSTATUSREQUESTS) { uiInfo.nextFindPlayerRefresh = uiInfo.uiDC.realTime + 25; } else { // add a line that shows the number of servers found if (!uiInfo.numFoundPlayerServers) { Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]), "no servers found"); } else { Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]), "%d server%s found with player %s", uiInfo.numFoundPlayerServers-1, uiInfo.numFoundPlayerServers == 2 ? "":"s", uiInfo.findPlayerName); } uiInfo.nextFindPlayerRefresh = 0; // show the server status info for the selected server UI_FeederSelection(FEEDER_FINDPLAYER, uiInfo.currentFoundPlayerServer); } } /* ================== UI_BuildServerStatus ================== */ static void UI_BuildServerStatus(qboolean force) { if (uiInfo.nextFindPlayerRefresh) { return; } if (!force) { if (!uiInfo.nextServerStatusRefresh || uiInfo.nextServerStatusRefresh > uiInfo.uiDC.realTime) { return; } } else { Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL); uiInfo.serverStatusInfo.numLines = 0; // reset all server status requests trap_LAN_ServerStatus( NULL, NULL, 0); } if (uiInfo.serverStatus.currentServer < 0 || uiInfo.serverStatus.currentServer > uiInfo.serverStatus.numDisplayServers || uiInfo.serverStatus.numDisplayServers == 0) { return; } if (UI_GetServerStatusInfo( uiInfo.serverStatusAddress, &uiInfo.serverStatusInfo ) ) { uiInfo.nextServerStatusRefresh = 0; UI_GetServerStatusInfo( uiInfo.serverStatusAddress, NULL ); } else { uiInfo.nextServerStatusRefresh = uiInfo.uiDC.realTime + 500; } } /* ================== UI_FeederCount ================== */ static int UI_FeederCount(float feederID) { if (feederID == FEEDER_HEADS) { return UI_HeadCountByTeam(); } else if (feederID == FEEDER_Q3HEADS) { return uiInfo.q3HeadCount; } else if (feederID == FEEDER_CINEMATICS) { return uiInfo.movieCount; } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse); } else if (feederID == FEEDER_SERVERS) { return uiInfo.serverStatus.numDisplayServers; } else if (feederID == FEEDER_SERVERSTATUS) { return uiInfo.serverStatusInfo.numLines; } else if (feederID == FEEDER_FINDPLAYER) { return uiInfo.numFoundPlayerServers; } else if (feederID == FEEDER_PLAYER_LIST) { if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; UI_BuildPlayerList(); } return uiInfo.playerCount; } else if (feederID == FEEDER_TEAM_LIST) { if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; UI_BuildPlayerList(); } return uiInfo.myTeamCount; } else if (feederID == FEEDER_MODS) { return uiInfo.modCount; } else if (feederID == FEEDER_DEMOS) { return uiInfo.demoCount; } return 0; } static const char *UI_SelectedMap(int index, int *actual) { int i, c; c = 0; *actual = 0; for (i = 0; i < uiInfo.mapCount; i++) { if (uiInfo.mapList[i].active) { if (c == index) { *actual = i; return uiInfo.mapList[i].mapName; } else { c++; } } } return ""; } static const char *UI_SelectedHead(int index, int *actual) { int i, c; c = 0; *actual = 0; for (i = 0; i < uiInfo.characterCount; i++) { if (uiInfo.characterList[i].active) { if (c == index) { *actual = i; return uiInfo.characterList[i].name; } else { c++; } } } return ""; } static int UI_GetIndexFromSelection(int actual) { int i, c; c = 0; for (i = 0; i < uiInfo.mapCount; i++) { if (uiInfo.mapList[i].active) { if (i == actual) { return c; } c++; } } return 0; } static void UI_UpdatePendingPings( void ) { trap_LAN_ResetPings(ui_netSource.integer); uiInfo.serverStatus.refreshActive = qtrue; uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; } static const char *UI_FeederItemText(float feederID, int index, int column, qhandle_t *handle) { static char info[MAX_STRING_CHARS]; static char hostname[1024]; static char clientBuff[32]; static int lastColumn = -1; static int lastTime = 0; *handle = -1; if (feederID == FEEDER_HEADS) { int actual; return UI_SelectedHead(index, &actual); } else if (feederID == FEEDER_Q3HEADS) { if (index >= 0 && index < uiInfo.q3HeadCount) { return uiInfo.q3HeadNames[index]; } } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { int actual; return UI_SelectedMap(index, &actual); } else if (feederID == FEEDER_SERVERS) { if (index >= 0 && index < uiInfo.serverStatus.numDisplayServers) { int ping, game; if (lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000) { trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS); lastColumn = column; lastTime = uiInfo.uiDC.realTime; } ping = atoi(Info_ValueForKey(info, "ping")); if (ping == -1) { // if we ever see a ping that is out of date, do a server refresh // UI_UpdatePendingPings(); } switch (column) { case SORT_HOST : if (ping <= 0) { return Info_ValueForKey(info, "addr"); } else { if ( ui_netSource.integer == AS_LOCAL ) { Com_sprintf( hostname, sizeof(hostname), "%s [%s]", Info_ValueForKey(info, "hostname"), netnames[atoi(Info_ValueForKey(info, "nettype"))] ); return hostname; } else { Com_sprintf( hostname, sizeof(hostname), "%s", Info_ValueForKey(info, "hostname")); return hostname; } } case SORT_MAP : return Info_ValueForKey(info, "mapname"); case SORT_CLIENTS : Com_sprintf( clientBuff, sizeof(clientBuff), "%s (%s)", trap_Cvar_VariableValue("ui_humansonly")? Info_ValueForKey(info, "g_humanplayers") : Info_ValueForKey(info, "clients"), Info_ValueForKey(info, "sv_maxclients")); return clientBuff; case SORT_GAME : game = atoi(Info_ValueForKey(info, "gametype")); if (game >= 0 && game < numTeamArenaGameTypes) { return teamArenaGameTypes[game]; } else { return "Unknown"; } case SORT_PING : if (ping <= 0) { return "..."; } else { return Info_ValueForKey(info, "ping"); } } } } else if (feederID == FEEDER_SERVERSTATUS) { if ( index >= 0 && index < uiInfo.serverStatusInfo.numLines ) { if ( column >= 0 && column < 4 ) { return uiInfo.serverStatusInfo.lines[index][column]; } } } else if (feederID == FEEDER_FINDPLAYER) { if ( index >= 0 && index < uiInfo.numFoundPlayerServers ) { //return uiInfo.foundPlayerServerAddresses[index]; return uiInfo.foundPlayerServerNames[index]; } } else if (feederID == FEEDER_PLAYER_LIST) { if (index >= 0 && index < uiInfo.playerCount) { return uiInfo.playerNames[index]; } } else if (feederID == FEEDER_TEAM_LIST) { if (index >= 0 && index < uiInfo.myTeamCount) { return uiInfo.teamNames[index]; } } else if (feederID == FEEDER_MODS) { if (index >= 0 && index < uiInfo.modCount) { if (uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr) { return uiInfo.modList[index].modDescr; } else { return uiInfo.modList[index].modName; } } } else if (feederID == FEEDER_CINEMATICS) { if (index >= 0 && index < uiInfo.movieCount) { return uiInfo.movieList[index]; } } else if (feederID == FEEDER_DEMOS) { if (index >= 0 && index < uiInfo.demoCount) { return uiInfo.demoList[index]; } } return ""; } static qhandle_t UI_FeederItemImage(float feederID, int index) { if (feederID == FEEDER_HEADS) { int actual; UI_SelectedHead(index, &actual); index = actual; if (index >= 0 && index < uiInfo.characterCount) { if (uiInfo.characterList[index].headImage == -1) { uiInfo.characterList[index].headImage = trap_R_RegisterShaderNoMip(uiInfo.characterList[index].imageName); } return uiInfo.characterList[index].headImage; } } else if (feederID == FEEDER_Q3HEADS) { if (index >= 0 && index < uiInfo.q3HeadCount) { return uiInfo.q3HeadIcons[index]; } } else if (feederID == FEEDER_ALLMAPS || feederID == FEEDER_MAPS) { int actual; UI_SelectedMap(index, &actual); index = actual; if (index >= 0 && index < uiInfo.mapCount) { if (uiInfo.mapList[index].levelShot == -1) { uiInfo.mapList[index].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[index].imageName); } return uiInfo.mapList[index].levelShot; } } return 0; } static void UI_FeederSelection(float feederID, int index) { static char info[MAX_STRING_CHARS]; if (feederID == FEEDER_HEADS) { int actual; UI_SelectedHead(index, &actual); index = actual; if (index >= 0 && index < uiInfo.characterCount) { trap_Cvar_Set( "team_model", va("%s", uiInfo.characterList[index].base)); trap_Cvar_Set( "team_headmodel", va("*%s", uiInfo.characterList[index].name)); updateModel = qtrue; } } else if (feederID == FEEDER_Q3HEADS) { if (index >= 0 && index < uiInfo.q3HeadCount) { trap_Cvar_Set( "model", uiInfo.q3HeadNames[index]); trap_Cvar_Set( "headmodel", uiInfo.q3HeadNames[index]); updateModel = qtrue; } } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { int actual, map; map = (feederID == FEEDER_ALLMAPS) ? ui_currentNetMap.integer : ui_currentMap.integer; if (uiInfo.mapList[map].cinematic >= 0) { trap_CIN_StopCinematic(uiInfo.mapList[map].cinematic); uiInfo.mapList[map].cinematic = -1; } UI_SelectedMap(index, &actual); trap_Cvar_Set("ui_mapIndex", va("%d", index)); ui_mapIndex.integer = index; if (feederID == FEEDER_MAPS) { ui_currentMap.integer = actual; trap_Cvar_Set("ui_currentMap", va("%d", actual)); uiInfo.mapList[ui_currentMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); trap_Cvar_Set("ui_opponentModel", uiInfo.mapList[ui_currentMap.integer].opponentName); updateOpponentModel = qtrue; } else { ui_currentNetMap.integer = actual; trap_Cvar_Set("ui_currentNetMap", va("%d", actual)); uiInfo.mapList[ui_currentNetMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); } } else if (feederID == FEEDER_SERVERS) { const char *mapName = NULL; uiInfo.serverStatus.currentServer = index; trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS); uiInfo.serverStatus.currentServerPreview = trap_R_RegisterShaderNoMip(va("levelshots/%s", Info_ValueForKey(info, "mapname"))); if (uiInfo.serverStatus.currentServerCinematic >= 0) { trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic); uiInfo.serverStatus.currentServerCinematic = -1; } mapName = Info_ValueForKey(info, "mapname"); if (mapName && *mapName) { uiInfo.serverStatus.currentServerCinematic = trap_CIN_PlayCinematic(va("%s.roq", mapName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); } } else if (feederID == FEEDER_SERVERSTATUS) { // } else if (feederID == FEEDER_FINDPLAYER) { uiInfo.currentFoundPlayerServer = index; // if ( index < uiInfo.numFoundPlayerServers-1) { // build a new server status for this server Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress)); Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL); UI_BuildServerStatus(qtrue); } } else if (feederID == FEEDER_PLAYER_LIST) { uiInfo.playerIndex = index; } else if (feederID == FEEDER_TEAM_LIST) { uiInfo.teamIndex = index; } else if (feederID == FEEDER_MODS) { uiInfo.modIndex = index; } else if (feederID == FEEDER_CINEMATICS) { uiInfo.movieIndex = index; if (uiInfo.previewMovie >= 0) { trap_CIN_StopCinematic(uiInfo.previewMovie); } uiInfo.previewMovie = -1; } else if (feederID == FEEDER_DEMOS) { uiInfo.demoIndex = index; } } static qboolean Team_Parse(char **p) { char *token; const char *tempStr; int i; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } if (token[0] == '{') { // seven tokens per line, team name and icon, and 5 team member names if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamName) || !String_Parse(p, &tempStr)) { return qfalse; } uiInfo.teamList[uiInfo.teamCount].imageName = tempStr; uiInfo.teamList[uiInfo.teamCount].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[uiInfo.teamCount].imageName); uiInfo.teamList[uiInfo.teamCount].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[uiInfo.teamCount].imageName)); uiInfo.teamList[uiInfo.teamCount].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[uiInfo.teamCount].imageName)); uiInfo.teamList[uiInfo.teamCount].cinematic = -1; for (i = 0; i < TEAM_MEMBERS; i++) { uiInfo.teamList[uiInfo.teamCount].teamMembers[i] = NULL; if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamMembers[i])) { return qfalse; } } Com_Printf("Loaded team %s with team icon %s.\n", uiInfo.teamList[uiInfo.teamCount].teamName, tempStr); if (uiInfo.teamCount < MAX_TEAMS) { uiInfo.teamCount++; } else { Com_Printf("Too many teams, last team replaced!\n"); } token = COM_ParseExt(p, qtrue); if (token[0] != '}') { return qfalse; } } } return qfalse; } static qboolean Character_Parse(char **p) { char *token; const char *tempStr; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } if (token[0] == '{') { // two tokens per line, character name and sex if (!String_Parse(p, &uiInfo.characterList[uiInfo.characterCount].name) || !String_Parse(p, &tempStr)) { return qfalse; } uiInfo.characterList[uiInfo.characterCount].headImage = -1; uiInfo.characterList[uiInfo.characterCount].imageName = String_Alloc(va("models/players/heads/%s/icon_default.tga", uiInfo.characterList[uiInfo.characterCount].name)); if (tempStr && (!Q_stricmp(tempStr, "female"))) { uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("kyonshi")); } else if (tempStr && (!Q_stricmp(tempStr, "male"))) { uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("sergei")); } else { uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("%s",tempStr)); } Com_Printf("Loaded %s character %s.\n", uiInfo.characterList[uiInfo.characterCount].base, uiInfo.characterList[uiInfo.characterCount].name); if (uiInfo.characterCount < MAX_HEADS) { uiInfo.characterCount++; } else { Com_Printf("Too many characters, last character replaced!\n"); } token = COM_ParseExt(p, qtrue); if (token[0] != '}') { return qfalse; } } } return qfalse; } static qboolean Alias_Parse(char **p) { char *token; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } if (token[0] == '{') { // three tokens per line, character name, bot alias, and preferred action a - all purpose, d - defense, o - offense if (!String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].name) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].ai) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].action)) { return qfalse; } Com_Printf("Loaded character alias %s using character ai %s.\n", uiInfo.aliasList[uiInfo.aliasCount].name, uiInfo.aliasList[uiInfo.aliasCount].ai); if (uiInfo.aliasCount < MAX_ALIASES) { uiInfo.aliasCount++; } else { Com_Printf("Too many aliases, last alias replaced!\n"); } token = COM_ParseExt(p, qtrue); if (token[0] != '}') { return qfalse; } } } return qfalse; } // mode // 0 - high level parsing // 1 - team parsing // 2 - character parsing static void UI_ParseTeamInfo(const char *teamFile) { char *token; char *p; char *buff = NULL; //static int mode = 0; TTimo: unused buff = GetMenuBuffer(teamFile); if (!buff) { return; } p = buff; while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if( !token || token[0] == 0 || token[0] == '}') { break; } if ( Q_stricmp( token, "}" ) == 0 ) { break; } if (Q_stricmp(token, "teams") == 0) { if (Team_Parse(&p)) { continue; } else { break; } } if (Q_stricmp(token, "characters") == 0) { Character_Parse(&p); } if (Q_stricmp(token, "aliases") == 0) { Alias_Parse(&p); } } } static qboolean GameType_Parse(char **p, qboolean join) { char *token; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } if (join) { uiInfo.numJoinGameTypes = 0; } else { uiInfo.numGameTypes = 0; } while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } if (token[0] == '{') { // two tokens per line, character name and sex if (join) { if (!String_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gameType) || !Int_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gtEnum)) { return qfalse; } } else { if (!String_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gameType) || !Int_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gtEnum)) { return qfalse; } } if (join) { if (uiInfo.numJoinGameTypes < MAX_GAMETYPES) { uiInfo.numJoinGameTypes++; } else { Com_Printf("Too many net game types, last one replace!\n"); } } else { if (uiInfo.numGameTypes < MAX_GAMETYPES) { uiInfo.numGameTypes++; } else { Com_Printf("Too many game types, last one replace!\n"); } } token = COM_ParseExt(p, qtrue); if (token[0] != '}') { return qfalse; } } } return qfalse; } static qboolean MapList_Parse(char **p) { char *token; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } uiInfo.mapCount = 0; while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } if (token[0] == '{') { if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapName) || !String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapLoadName) ||!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].teamMembers) ) { return qfalse; } if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].opponentName)) { return qfalse; } uiInfo.mapList[uiInfo.mapCount].typeBits = 0; while (1) { token = COM_ParseExt(p, qtrue); if (token[0] >= '0' && token[0] <= '9') { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << (token[0] - 0x030)); if (!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].timeToBeat[token[0] - 0x30])) { return qfalse; } } else { break; } } //mapList[mapCount].imageName = String_Alloc(va("levelshots/%s", mapList[mapCount].mapLoadName)); //if (uiInfo.mapCount == 0) { // only load the first cinematic, selection loads the others // uiInfo.mapList[uiInfo.mapCount].cinematic = trap_CIN_PlayCinematic(va("%s.roq",uiInfo.mapList[uiInfo.mapCount].mapLoadName), qfalse, qfalse, qtrue, 0, 0, 0, 0); //} uiInfo.mapList[uiInfo.mapCount].cinematic = -1; uiInfo.mapList[uiInfo.mapCount].levelShot = trap_R_RegisterShaderNoMip(va("levelshots/%s_small", uiInfo.mapList[uiInfo.mapCount].mapLoadName)); if (uiInfo.mapCount < MAX_MAPS) { uiInfo.mapCount++; } else { Com_Printf("Too many maps, last one replaced!\n"); } } } return qfalse; } static void UI_ParseGameInfo(const char *teamFile) { char *token; char *p; char *buff = NULL; //int mode = 0; TTimo: unused buff = GetMenuBuffer(teamFile); if (!buff) { return; } p = buff; while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if( !token || token[0] == 0 || token[0] == '}') { break; } if ( Q_stricmp( token, "}" ) == 0 ) { break; } if (Q_stricmp(token, "gametypes") == 0) { if (GameType_Parse(&p, qfalse)) { continue; } else { break; } } if (Q_stricmp(token, "joingametypes") == 0) { if (GameType_Parse(&p, qtrue)) { continue; } else { break; } } if (Q_stricmp(token, "maps") == 0) { // start a new menu MapList_Parse(&p); } } } static void UI_Pause(qboolean b) { if (b) { // pause the game and set the ui keycatcher trap_Cvar_Set( "cl_paused", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); } else { // unpause the game and clear the ui keycatcher trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); } } #ifndef MISSIONPACK // bk001206 static int UI_OwnerDraw_Width(int ownerDraw) { // bk001205 - LCC missing return value return 0; } #endif static int UI_PlayCinematic(const char *name, float x, float y, float w, float h) { return trap_CIN_PlayCinematic(name, x, y, w, h, (CIN_loop | CIN_silent)); } static void UI_StopCinematic(int handle) { if (handle >= 0) { trap_CIN_StopCinematic(handle); } else { handle = abs(handle); if (handle == UI_MAPCINEMATIC) { if (uiInfo.mapList[ui_currentMap.integer].cinematic >= 0) { trap_CIN_StopCinematic(uiInfo.mapList[ui_currentMap.integer].cinematic); uiInfo.mapList[ui_currentMap.integer].cinematic = -1; } } else if (handle == UI_NETMAPCINEMATIC) { if (uiInfo.serverStatus.currentServerCinematic >= 0) { trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic); uiInfo.serverStatus.currentServerCinematic = -1; } } else if (handle == UI_CLANCINEMATIC) { int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); if (i >= 0 && i < uiInfo.teamCount) { if (uiInfo.teamList[i].cinematic >= 0) { trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic); uiInfo.teamList[i].cinematic = -1; } } } } } static void UI_DrawCinematic(int handle, float x, float y, float w, float h) { trap_CIN_SetExtents(handle, x, y, w, h); trap_CIN_DrawCinematic(handle); } static void UI_RunCinematicFrame(int handle) { trap_CIN_RunCinematic(handle); } /* ================= PlayerModel_BuildList ================= */ static void UI_BuildQ3Model_List( void ) { int numdirs; int numfiles; char dirlist[2048]; char filelist[2048]; char skinname[MAX_QPATH]; char scratch[256]; char* dirptr; char* fileptr; int i; int j, k, dirty; int dirlen; int filelen; uiInfo.q3HeadCount = 0; // iterate directory of all player models numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 32768 ); // upped from 2048 dirptr = dirlist; for (i=0; i uiInfo.uiDC.glconfig.vidHeight * 640 ) { // wide screen uiInfo.uiDC.bias = 0.5 * ( uiInfo.uiDC.glconfig.vidWidth - ( uiInfo.uiDC.glconfig.vidHeight * (640.0/480.0) ) ); } else { // no wide screen uiInfo.uiDC.bias = 0; } //UI_Load(); uiInfo.uiDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip; uiInfo.uiDC.setColor = &UI_SetColor; uiInfo.uiDC.drawHandlePic = &UI_DrawHandlePic; uiInfo.uiDC.drawStretchPic = &trap_R_DrawStretchPic; uiInfo.uiDC.drawText = &Text_Paint; uiInfo.uiDC.textWidth = &Text_Width; uiInfo.uiDC.textHeight = &Text_Height; uiInfo.uiDC.registerModel = &trap_R_RegisterModel; uiInfo.uiDC.modelBounds = &trap_R_ModelBounds; uiInfo.uiDC.fillRect = &UI_FillRect; uiInfo.uiDC.drawRect = &_UI_DrawRect; uiInfo.uiDC.drawSides = &_UI_DrawSides; uiInfo.uiDC.drawTopBottom = &_UI_DrawTopBottom; uiInfo.uiDC.clearScene = &trap_R_ClearScene; uiInfo.uiDC.drawSides = &_UI_DrawSides; uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; uiInfo.uiDC.renderScene = &trap_R_RenderScene; uiInfo.uiDC.registerFont = &trap_R_RegisterFont; uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw; uiInfo.uiDC.getValue = &UI_GetValue; uiInfo.uiDC.ownerDrawVisible = &UI_OwnerDrawVisible; uiInfo.uiDC.runScript = &UI_RunMenuScript; uiInfo.uiDC.getTeamColor = &UI_GetTeamColor; uiInfo.uiDC.setCVar = trap_Cvar_Set; uiInfo.uiDC.getCVarString = trap_Cvar_VariableStringBuffer; uiInfo.uiDC.getCVarValue = trap_Cvar_VariableValue; uiInfo.uiDC.drawTextWithCursor = &Text_PaintWithCursor; uiInfo.uiDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode; uiInfo.uiDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode; uiInfo.uiDC.startLocalSound = &trap_S_StartLocalSound; uiInfo.uiDC.ownerDrawHandleKey = &UI_OwnerDrawHandleKey; uiInfo.uiDC.feederCount = &UI_FeederCount; uiInfo.uiDC.feederItemImage = &UI_FeederItemImage; uiInfo.uiDC.feederItemText = &UI_FeederItemText; uiInfo.uiDC.feederSelection = &UI_FeederSelection; uiInfo.uiDC.setBinding = &trap_Key_SetBinding; uiInfo.uiDC.getBindingBuf = &trap_Key_GetBindingBuf; uiInfo.uiDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf; uiInfo.uiDC.executeText = &trap_Cmd_ExecuteText; uiInfo.uiDC.Error = &Com_Error; uiInfo.uiDC.Print = &Com_Printf; uiInfo.uiDC.Pause = &UI_Pause; uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth; uiInfo.uiDC.registerSound = &trap_S_RegisterSound; uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; uiInfo.uiDC.playCinematic = &UI_PlayCinematic; uiInfo.uiDC.stopCinematic = &UI_StopCinematic; uiInfo.uiDC.drawCinematic = &UI_DrawCinematic; uiInfo.uiDC.runCinematicFrame = &UI_RunCinematicFrame; Init_Display(&uiInfo.uiDC); String_Init(); uiInfo.uiDC.cursor = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" ); uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip( "white" ); AssetCache(); start = trap_Milliseconds(); uiInfo.teamCount = 0; uiInfo.characterCount = 0; uiInfo.aliasCount = 0; #ifdef PRE_RELEASE_TADEMO UI_ParseTeamInfo("demoteaminfo.txt"); UI_ParseGameInfo("demogameinfo.txt"); #else UI_ParseTeamInfo("teaminfo.txt"); UI_LoadTeams(); UI_ParseGameInfo("gameinfo.txt"); #endif menuSet = UI_Cvar_VariableString("ui_menuFiles"); if (menuSet == NULL || menuSet[0] == '\0') { menuSet = "ui/menus.txt"; } #if 0 if (uiInfo.inGameLoad) { UI_LoadMenus("ui/ingame.txt", qtrue); } else { // bk010222: left this: UI_LoadMenus(menuSet, qtrue); } #else UI_LoadMenus(menuSet, qtrue); UI_LoadMenus("ui/ingame.txt", qfalse); #endif Menus_CloseAll(); trap_LAN_LoadCachedServers(); UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); UI_BuildQ3Model_List(); UI_LoadBots(); // sets defaults for ui temp cvars uiInfo.effectsColor = gamecodetoui[(int)trap_Cvar_VariableValue("color1")-1]; uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair"); trap_Cvar_Set("ui_mousePitch", (trap_Cvar_VariableValue("m_pitch") >= 0) ? "0" : "1"); uiInfo.serverStatus.currentServerCinematic = -1; uiInfo.previewMovie = -1; if (trap_Cvar_VariableValue("ui_TeamArenaFirstRun") == 0) { trap_Cvar_Set("s_volume", "0.8"); trap_Cvar_Set("s_musicvolume", "0.5"); trap_Cvar_Set("ui_TeamArenaFirstRun", "1"); } trap_Cvar_Register(NULL, "debug_protocol", "", 0 ); trap_Cvar_Set("ui_actualNetGameType", va("%d", ui_netGameType.integer)); } /* ================= UI_KeyEvent ================= */ void _UI_KeyEvent( int key, qboolean down ) { if (Menu_Count() > 0) { menuDef_t *menu = Menu_GetFocused(); if (menu) { if (key == K_ESCAPE && down && !Menus_AnyFullScreenVisible()) { Menus_CloseAll(); } else { Menu_HandleKey(menu, key, down ); } } else { trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); } } //if ((s > 0) && (s != menu_null_sound)) { // trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND ); //} } /* ================= UI_MouseEvent ================= */ void _UI_MouseEvent( int dx, int dy ) { // update mouse screen position uiInfo.uiDC.cursorx += dx; if (uiInfo.uiDC.cursorx < 0) uiInfo.uiDC.cursorx = 0; else if (uiInfo.uiDC.cursorx > SCREEN_WIDTH) uiInfo.uiDC.cursorx = SCREEN_WIDTH; uiInfo.uiDC.cursory += dy; if (uiInfo.uiDC.cursory < 0) uiInfo.uiDC.cursory = 0; else if (uiInfo.uiDC.cursory > SCREEN_HEIGHT) uiInfo.uiDC.cursory = SCREEN_HEIGHT; if (Menu_Count() > 0) { //menuDef_t *menu = Menu_GetFocused(); //Menu_HandleMouseMove(menu, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory); Display_MouseMove(NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory); } } void UI_LoadNonIngame( void ) { const char *menuSet = UI_Cvar_VariableString("ui_menuFiles"); if (menuSet == NULL || menuSet[0] == '\0') { menuSet = "ui/menus.txt"; } UI_LoadMenus(menuSet, qfalse); uiInfo.inGameLoad = qfalse; } void _UI_SetActiveMenu( uiMenuCommand_t menu ) { char buf[256]; // this should be the ONLY way the menu system is brought up // enusure minumum menu data is cached if (Menu_Count() > 0) { vec3_t v; v[0] = v[1] = v[2] = 0; switch ( menu ) { case UIMENU_NONE: trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); Menus_CloseAll(); return; case UIMENU_MAIN: trap_Cvar_Set( "sv_killserver", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); //trap_S_StartLocalSound( trap_S_RegisterSound("sound/misc/menu_background.wav", qfalse) , CHAN_LOCAL_SOUND ); //trap_S_StartBackgroundTrack("sound/misc/menu_background.wav", NULL); if (uiInfo.inGameLoad) { UI_LoadNonIngame(); } Menus_CloseAll(); Menus_ActivateByName("main"); trap_Cvar_VariableStringBuffer("com_errorMessage", buf, sizeof(buf)); if (strlen(buf)) { if (!ui_singlePlayerActive.integer) { Menus_ActivateByName("error_popmenu"); } else { trap_Cvar_Set("com_errorMessage", ""); } } return; case UIMENU_TEAM: trap_Key_SetCatcher( KEYCATCH_UI ); Menus_ActivateByName("team"); return; case UIMENU_NEED_CD: // no cd check in TA //trap_Key_SetCatcher( KEYCATCH_UI ); //Menus_ActivateByName("needcd"); //UI_ConfirmMenu( "Insert the CD", NULL, NeedCDAction ); return; case UIMENU_BAD_CD_KEY: // no cd check in TA //trap_Key_SetCatcher( KEYCATCH_UI ); //Menus_ActivateByName("badcd"); //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); return; case UIMENU_POSTGAME: trap_Cvar_Set( "sv_killserver", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); if (uiInfo.inGameLoad) { UI_LoadNonIngame(); } Menus_CloseAll(); Menus_ActivateByName("endofgame"); //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); return; case UIMENU_INGAME: trap_Cvar_Set( "cl_paused", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); UI_BuildPlayerList(); Menus_CloseAll(); Menus_ActivateByName("ingame"); return; } } } qboolean _UI_IsFullscreen( void ) { return Menus_AnyFullScreenVisible(); } static connstate_t lastConnState; static char lastLoadingText[MAX_INFO_VALUE]; static void UI_ReadableSize ( char *buf, int bufsize, int value ) { if (value > 1024*1024*1024 ) { // gigs Com_sprintf( buf, bufsize, "%d", value / (1024*1024*1024) ); Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d GB", (value % (1024*1024*1024))*100 / (1024*1024*1024) ); } else if (value > 1024*1024 ) { // megs Com_sprintf( buf, bufsize, "%d", value / (1024*1024) ); Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d MB", (value % (1024*1024))*100 / (1024*1024) ); } else if (value > 1024 ) { // kilos Com_sprintf( buf, bufsize, "%d KB", value / 1024 ); } else { // bytes Com_sprintf( buf, bufsize, "%d bytes", value ); } } // Assumes time is in msec static void UI_PrintTime ( char *buf, int bufsize, int time ) { time /= 1000; // change to seconds if (time > 3600) { // in the hours range Com_sprintf( buf, bufsize, "%d hr %d min", time / 3600, (time % 3600) / 60 ); } else if (time > 60) { // mins Com_sprintf( buf, bufsize, "%d min %d sec", time / 60, time % 60 ); } else { // secs Com_sprintf( buf, bufsize, "%d sec", time ); } } void Text_PaintCenter(float x, float y, float scale, vec4_t color, const char *text, float adjust) { int len = Text_Width(text, scale, 0); Text_Paint(x - len / 2, y, scale, color, text, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); } void Text_PaintCenter_AutoWrapped(float x, float y, float xmax, float ystep, float scale, vec4_t color, const char *str, float adjust) { int width; char *s1,*s2,*s3; char c_bcp; char buf[1024]; if (!str || str[0]=='\0') return; Q_strncpyz(buf, str, sizeof(buf)); s1 = s2 = s3 = buf; while (1) { do { s3++; } while (*s3!=' ' && *s3!='\0'); c_bcp = *s3; *s3 = '\0'; width = Text_Width(s1, scale, 0); *s3 = c_bcp; if (width > xmax) { if (s1==s2) { // fuck, don't have a clean cut, we'll overflow s2 = s3; } *s2 = '\0'; Text_PaintCenter(x, y, scale, color, s1, adjust); y += ystep; if (c_bcp == '\0') { // that was the last word // we could start a new loop, but that wouldn't be much use // even if the word is too long, we would overflow it (see above) // so just print it now if needed s2++; if (*s2 != '\0') // if we are printing an overflowing line we have s2 == s3 Text_PaintCenter(x, y, scale, color, s2, adjust); break; } s2++; s1 = s2; s3 = s2; } else { s2 = s3; if (c_bcp == '\0') // we reached the end { Text_PaintCenter(x, y, scale, color, s1, adjust); break; } } } } static void UI_DisplayDownloadInfo( const char *downloadName, float centerPoint, float yStart, float scale ) { static char dlText[] = "Downloading:"; static char etaText[] = "Estimated time left:"; static char xferText[] = "Transfer rate:"; int downloadSize, downloadCount, downloadTime; char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64]; int xferRate; int leftWidth; const char *s; downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" ); downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" ); downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" ); leftWidth = 320; UI_SetColor(colorWhite); Text_PaintCenter(centerPoint, yStart + 112, scale, colorWhite, dlText, 0); Text_PaintCenter(centerPoint, yStart + 192, scale, colorWhite, etaText, 0); Text_PaintCenter(centerPoint, yStart + 248, scale, colorWhite, xferText, 0); if (downloadSize > 0) { s = va( "%s (%d%%)", downloadName, (int)( (float)downloadCount * 100.0f / downloadSize ) ); } else { s = downloadName; } Text_PaintCenter(centerPoint, yStart+136, scale, colorWhite, s, 0); UI_ReadableSize( dlSizeBuf, sizeof dlSizeBuf, downloadCount ); UI_ReadableSize( totalSizeBuf, sizeof totalSizeBuf, downloadSize ); if (downloadCount < 4096 || !downloadTime) { Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0); Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); } else { if ((uiInfo.uiDC.realTime - downloadTime) / 1000) { xferRate = downloadCount / ((uiInfo.uiDC.realTime - downloadTime) / 1000); } else { xferRate = 0; } UI_ReadableSize( xferRateBuf, sizeof xferRateBuf, xferRate ); // Extrapolate estimated completion time if (downloadSize && xferRate) { int n = downloadSize / xferRate; // estimated time for entire d/l in secs // We do it in K (/1024) because we'd overflow around 4MB UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf, (n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000); Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, dlTimeBuf, 0); Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); } else { Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0); if (downloadSize) { Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); } else { Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s copied)", dlSizeBuf), 0); } } if (xferRate) { Text_PaintCenter(leftWidth, yStart+272, scale, colorWhite, va("%s/Sec", xferRateBuf), 0); } } } /* ======================== UI_DrawConnectScreen This will also be overlaid on the cgame info screen during loading to prevent it from blinking away too rapidly on local or lan games. ======================== */ void UI_DrawConnectScreen( qboolean overlay ) { char *s; uiClientState_t cstate; char info[MAX_INFO_VALUE]; char text[256]; float centerPoint, yStart, scale; menuDef_t *menu = Menus_FindByName("Connect"); if ( !overlay && menu ) { Menu_Paint(menu, qtrue); } if (!overlay) { centerPoint = 320; yStart = 130; scale = 0.5f; } else { centerPoint = 320; yStart = 32; scale = 0.6f; return; } // see what information we should display trap_GetClientState( &cstate ); info[0] = '\0'; if( trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ) ) { Text_PaintCenter(centerPoint, yStart, scale, colorWhite, va( "Loading %s", Info_ValueForKey( info, "mapname" )), 0); } if (!Q_stricmp(cstate.servername,"localhost")) { Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite, va("Starting up..."), ITEM_TEXTSTYLE_SHADOWEDMORE); } else { strcpy(text, va("Connecting to %s", cstate.servername)); Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite,text , ITEM_TEXTSTYLE_SHADOWEDMORE); } // display global MOTD at bottom Text_PaintCenter(centerPoint, 600, scale, colorWhite, Info_ValueForKey( cstate.updateInfoString, "motd" ), 0); // print any server info (server full, bad version, etc) if ( cstate.connState < CA_CONNECTED ) { Text_PaintCenter_AutoWrapped(centerPoint, yStart + 176, 630, 20, scale, colorWhite, cstate.messageString, 0); } if ( lastConnState > cstate.connState ) { lastLoadingText[0] = '\0'; } lastConnState = cstate.connState; switch ( cstate.connState ) { case CA_CONNECTING: s = va("Awaiting connection...%i", cstate.connectPacketCount); break; case CA_CHALLENGING: s = va("Awaiting challenge...%i", cstate.connectPacketCount); break; case CA_CONNECTED: { char downloadName[MAX_INFO_VALUE]; trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof(downloadName) ); if (*downloadName) { UI_DisplayDownloadInfo( downloadName, centerPoint, yStart, scale ); return; } } s = "Awaiting gamestate..."; break; case CA_LOADING: return; case CA_PRIMED: return; default: return; } if (Q_stricmp(cstate.servername,"localhost")) { Text_PaintCenter(centerPoint, yStart + 80, scale, colorWhite, s, 0); } // password required / connection rejected information goes here } /* ================ cvars ================ */ typedef struct { vmCvar_t *vmCvar; char *cvarName; char *defaultString; int cvarFlags; } cvarTable_t; vmCvar_t ui_ffa_fraglimit; vmCvar_t ui_ffa_timelimit; vmCvar_t ui_tourney_fraglimit; vmCvar_t ui_tourney_timelimit; vmCvar_t ui_team_fraglimit; vmCvar_t ui_team_timelimit; vmCvar_t ui_team_friendly; vmCvar_t ui_ctf_capturelimit; vmCvar_t ui_ctf_timelimit; vmCvar_t ui_ctf_friendly; vmCvar_t ui_arenasFile; vmCvar_t ui_botsFile; vmCvar_t ui_spScores1; vmCvar_t ui_spScores2; vmCvar_t ui_spScores3; vmCvar_t ui_spScores4; vmCvar_t ui_spScores5; vmCvar_t ui_spAwards; vmCvar_t ui_spVideos; vmCvar_t ui_spSkill; vmCvar_t ui_spSelection; vmCvar_t ui_browserMaster; vmCvar_t ui_browserGameType; vmCvar_t ui_browserSortKey; vmCvar_t ui_browserShowFull; vmCvar_t ui_browserShowEmpty; vmCvar_t ui_brassTime; vmCvar_t ui_drawCrosshair; vmCvar_t ui_drawCrosshairNames; vmCvar_t ui_marks; vmCvar_t ui_server1; vmCvar_t ui_server2; vmCvar_t ui_server3; vmCvar_t ui_server4; vmCvar_t ui_server5; vmCvar_t ui_server6; vmCvar_t ui_server7; vmCvar_t ui_server8; vmCvar_t ui_server9; vmCvar_t ui_server10; vmCvar_t ui_server11; vmCvar_t ui_server12; vmCvar_t ui_server13; vmCvar_t ui_server14; vmCvar_t ui_server15; vmCvar_t ui_server16; vmCvar_t ui_cdkeychecked; vmCvar_t ui_redteam; vmCvar_t ui_redteam1; vmCvar_t ui_redteam2; vmCvar_t ui_redteam3; vmCvar_t ui_redteam4; vmCvar_t ui_redteam5; vmCvar_t ui_blueteam; vmCvar_t ui_blueteam1; vmCvar_t ui_blueteam2; vmCvar_t ui_blueteam3; vmCvar_t ui_blueteam4; vmCvar_t ui_blueteam5; vmCvar_t ui_teamName; vmCvar_t ui_dedicated; vmCvar_t ui_gameType; vmCvar_t ui_netGameType; vmCvar_t ui_actualNetGameType; vmCvar_t ui_joinGameType; vmCvar_t ui_netSource; vmCvar_t ui_serverFilterType; vmCvar_t ui_opponentName; vmCvar_t ui_menuFiles; vmCvar_t ui_currentTier; vmCvar_t ui_currentMap; vmCvar_t ui_currentNetMap; vmCvar_t ui_mapIndex; vmCvar_t ui_currentOpponent; vmCvar_t ui_selectedPlayer; vmCvar_t ui_selectedPlayerName; vmCvar_t ui_lastServerRefresh_0; vmCvar_t ui_lastServerRefresh_1; vmCvar_t ui_lastServerRefresh_2; vmCvar_t ui_lastServerRefresh_3; vmCvar_t ui_singlePlayerActive; vmCvar_t ui_scoreAccuracy; vmCvar_t ui_scoreImpressives; vmCvar_t ui_scoreExcellents; vmCvar_t ui_scoreCaptures; vmCvar_t ui_scoreDefends; vmCvar_t ui_scoreAssists; vmCvar_t ui_scoreGauntlets; vmCvar_t ui_scoreScore; vmCvar_t ui_scorePerfect; vmCvar_t ui_scoreTeam; vmCvar_t ui_scoreBase; vmCvar_t ui_scoreTimeBonus; vmCvar_t ui_scoreSkillBonus; vmCvar_t ui_scoreShutoutBonus; vmCvar_t ui_scoreTime; vmCvar_t ui_captureLimit; vmCvar_t ui_fragLimit; vmCvar_t ui_smallFont; vmCvar_t ui_bigFont; vmCvar_t ui_findPlayer; vmCvar_t ui_Q3Model; vmCvar_t ui_hudFiles; vmCvar_t ui_recordSPDemo; vmCvar_t ui_realCaptureLimit; vmCvar_t ui_realWarmUp; vmCvar_t ui_serverStatusTimeOut; vmCvar_t ui_humansonly; // bk001129 - made static to avoid aliasing static cvarTable_t cvarTable[] = { { &ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", CVAR_ARCHIVE }, { &ui_ffa_timelimit, "ui_ffa_timelimit", "0", CVAR_ARCHIVE }, { &ui_tourney_fraglimit, "ui_tourney_fraglimit", "0", CVAR_ARCHIVE }, { &ui_tourney_timelimit, "ui_tourney_timelimit", "15", CVAR_ARCHIVE }, { &ui_team_fraglimit, "ui_team_fraglimit", "0", CVAR_ARCHIVE }, { &ui_team_timelimit, "ui_team_timelimit", "20", CVAR_ARCHIVE }, { &ui_team_friendly, "ui_team_friendly", "1", CVAR_ARCHIVE }, { &ui_ctf_capturelimit, "ui_ctf_capturelimit", "8", CVAR_ARCHIVE }, { &ui_ctf_timelimit, "ui_ctf_timelimit", "30", CVAR_ARCHIVE }, { &ui_ctf_friendly, "ui_ctf_friendly", "0", CVAR_ARCHIVE }, { &ui_arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM }, { &ui_botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM }, { &ui_spScores1, "g_spScores1", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE }, { &ui_spSelection, "ui_spSelection", "", CVAR_ROM }, { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE }, { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE }, { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE }, { &ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, { &ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, { &ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, { &ui_marks, "cg_marks", "1", CVAR_ARCHIVE }, { &ui_server1, "server1", "", CVAR_ARCHIVE }, { &ui_server2, "server2", "", CVAR_ARCHIVE }, { &ui_server3, "server3", "", CVAR_ARCHIVE }, { &ui_server4, "server4", "", CVAR_ARCHIVE }, { &ui_server5, "server5", "", CVAR_ARCHIVE }, { &ui_server6, "server6", "", CVAR_ARCHIVE }, { &ui_server7, "server7", "", CVAR_ARCHIVE }, { &ui_server8, "server8", "", CVAR_ARCHIVE }, { &ui_server9, "server9", "", CVAR_ARCHIVE }, { &ui_server10, "server10", "", CVAR_ARCHIVE }, { &ui_server11, "server11", "", CVAR_ARCHIVE }, { &ui_server12, "server12", "", CVAR_ARCHIVE }, { &ui_server13, "server13", "", CVAR_ARCHIVE }, { &ui_server14, "server14", "", CVAR_ARCHIVE }, { &ui_server15, "server15", "", CVAR_ARCHIVE }, { &ui_server16, "server16", "", CVAR_ARCHIVE }, { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, { &ui_new, "ui_new", "0", CVAR_TEMP }, { &ui_debug, "ui_debug", "0", CVAR_TEMP }, { &ui_initialized, "ui_initialized", "0", CVAR_TEMP }, { &ui_teamName, "ui_teamName", "Pagans", CVAR_ARCHIVE }, { &ui_opponentName, "ui_opponentName", "Stroggs", CVAR_ARCHIVE }, { &ui_redteam, "ui_redteam", "Pagans", CVAR_ARCHIVE }, { &ui_blueteam, "ui_blueteam", "Stroggs", CVAR_ARCHIVE }, { &ui_dedicated, "ui_dedicated", "0", CVAR_ARCHIVE }, { &ui_gameType, "ui_gametype", "3", CVAR_ARCHIVE }, { &ui_joinGameType, "ui_joinGametype", "0", CVAR_ARCHIVE }, { &ui_netGameType, "ui_netGametype", "3", CVAR_ARCHIVE }, { &ui_actualNetGameType, "ui_actualNetGametype", "3", CVAR_ARCHIVE }, { &ui_redteam1, "ui_redteam1", "0", CVAR_ARCHIVE }, { &ui_redteam2, "ui_redteam2", "0", CVAR_ARCHIVE }, { &ui_redteam3, "ui_redteam3", "0", CVAR_ARCHIVE }, { &ui_redteam4, "ui_redteam4", "0", CVAR_ARCHIVE }, { &ui_redteam5, "ui_redteam5", "0", CVAR_ARCHIVE }, { &ui_blueteam1, "ui_blueteam1", "0", CVAR_ARCHIVE }, { &ui_blueteam2, "ui_blueteam2", "0", CVAR_ARCHIVE }, { &ui_blueteam3, "ui_blueteam3", "0", CVAR_ARCHIVE }, { &ui_blueteam4, "ui_blueteam4", "0", CVAR_ARCHIVE }, { &ui_blueteam5, "ui_blueteam5", "0", CVAR_ARCHIVE }, { &ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE }, { &ui_menuFiles, "ui_menuFiles", "ui/menus.txt", CVAR_ARCHIVE }, { &ui_currentTier, "ui_currentTier", "0", CVAR_ARCHIVE }, { &ui_currentMap, "ui_currentMap", "0", CVAR_ARCHIVE }, { &ui_currentNetMap, "ui_currentNetMap", "0", CVAR_ARCHIVE }, { &ui_mapIndex, "ui_mapIndex", "0", CVAR_ARCHIVE }, { &ui_currentOpponent, "ui_currentOpponent", "0", CVAR_ARCHIVE }, { &ui_selectedPlayer, "cg_selectedPlayer", "0", CVAR_ARCHIVE}, { &ui_selectedPlayerName, "cg_selectedPlayerName", "", CVAR_ARCHIVE}, { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0", "", CVAR_ARCHIVE}, { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1", "", CVAR_ARCHIVE}, { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2", "", CVAR_ARCHIVE}, { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3", "", CVAR_ARCHIVE}, { &ui_singlePlayerActive, "ui_singlePlayerActive", "0", 0}, { &ui_scoreAccuracy, "ui_scoreAccuracy", "0", CVAR_ARCHIVE}, { &ui_scoreImpressives, "ui_scoreImpressives", "0", CVAR_ARCHIVE}, { &ui_scoreExcellents, "ui_scoreExcellents", "0", CVAR_ARCHIVE}, { &ui_scoreCaptures, "ui_scoreCaptures", "0", CVAR_ARCHIVE}, { &ui_scoreDefends, "ui_scoreDefends", "0", CVAR_ARCHIVE}, { &ui_scoreAssists, "ui_scoreAssists", "0", CVAR_ARCHIVE}, { &ui_scoreGauntlets, "ui_scoreGauntlets", "0",CVAR_ARCHIVE}, { &ui_scoreScore, "ui_scoreScore", "0", CVAR_ARCHIVE}, { &ui_scorePerfect, "ui_scorePerfect", "0", CVAR_ARCHIVE}, { &ui_scoreTeam, "ui_scoreTeam", "0 to 0", CVAR_ARCHIVE}, { &ui_scoreBase, "ui_scoreBase", "0", CVAR_ARCHIVE}, { &ui_scoreTime, "ui_scoreTime", "00:00", CVAR_ARCHIVE}, { &ui_scoreTimeBonus, "ui_scoreTimeBonus", "0", CVAR_ARCHIVE}, { &ui_scoreSkillBonus, "ui_scoreSkillBonus", "0", CVAR_ARCHIVE}, { &ui_scoreShutoutBonus, "ui_scoreShutoutBonus", "0", CVAR_ARCHIVE}, { &ui_fragLimit, "ui_fragLimit", "10", 0}, { &ui_captureLimit, "ui_captureLimit", "5", 0}, { &ui_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, { &ui_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, { &ui_findPlayer, "ui_findPlayer", "Sarge", CVAR_ARCHIVE}, { &ui_Q3Model, "ui_q3model", "0", CVAR_ARCHIVE}, { &ui_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE}, { &ui_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE}, { &ui_teamArenaFirstRun, "ui_teamArenaFirstRun", "0", CVAR_ARCHIVE}, { &ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE}, { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, { &ui_humansonly, "ui_humansonly", "0", CVAR_ARCHIVE}, }; // bk001129 - made static to avoid aliasing static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]); /* ================= UI_RegisterCvars ================= */ void UI_RegisterCvars( void ) { int i; cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); } } /* ================= UI_UpdateCvars ================= */ void UI_UpdateCvars( void ) { int i; cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Update( cv->vmCvar ); } } /* ================= ArenaServers_StopRefresh ================= */ static void UI_StopServerRefresh( void ) { int count; if (!uiInfo.serverStatus.refreshActive) { // not currently refreshing return; } uiInfo.serverStatus.refreshActive = qfalse; Com_Printf("%d servers listed in browser with %d players.\n", uiInfo.serverStatus.numDisplayServers, uiInfo.serverStatus.numPlayersOnServers); count = trap_LAN_GetServerCount(ui_netSource.integer); if (count - uiInfo.serverStatus.numDisplayServers > 0) { Com_Printf("%d servers not listed due to packet loss or pings higher than %d\n", count - uiInfo.serverStatus.numDisplayServers, (int) trap_Cvar_VariableValue("cl_maxPing")); } } /* ================= ArenaServers_MaxPing ================= */ #ifndef MISSIONPACK // bk001206 static int ArenaServers_MaxPing( void ) { int maxPing; maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" ); if( maxPing < 100 ) { maxPing = 100; } return maxPing; } #endif /* ================= UI_DoServerRefresh ================= */ static void UI_DoServerRefresh( void ) { qboolean wait = qfalse; if (!uiInfo.serverStatus.refreshActive) { return; } if (ui_netSource.integer != AS_FAVORITES) { if (ui_netSource.integer == AS_LOCAL) { if (!trap_LAN_GetServerCount(ui_netSource.integer)) { wait = qtrue; } } else { if (trap_LAN_GetServerCount(ui_netSource.integer) < 0) { wait = qtrue; } } } if (uiInfo.uiDC.realTime < uiInfo.serverStatus.refreshtime) { if (wait) { return; } } // if still trying to retrieve pings if (trap_LAN_UpdateVisiblePings(ui_netSource.integer)) { uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; } else if (!wait) { // get the last servers in the list UI_BuildServerDisplayList(2); // stop the refresh UI_StopServerRefresh(); } // UI_BuildServerDisplayList(qfalse); } /* ================= UI_StartServerRefresh ================= */ static void UI_StartServerRefresh(qboolean full) { char *ptr; qtime_t q; trap_RealTime(&q); trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); if (!full) { UI_UpdatePendingPings(); return; } uiInfo.serverStatus.refreshActive = qtrue; uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000; // clear number of displayed servers uiInfo.serverStatus.numDisplayServers = 0; uiInfo.serverStatus.numPlayersOnServers = 0; // mark all servers as visible so we store ping updates for them trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue); // reset all the pings trap_LAN_ResetPings(ui_netSource.integer); // if( ui_netSource.integer == AS_LOCAL ) { trap_Cmd_ExecuteText( EXEC_NOW, "localservers\n" ); uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; return; } uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000; if( ui_netSource.integer == AS_GLOBAL ) { ptr = UI_Cvar_VariableString("debug_protocol"); if (strlen(ptr)) { trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers 0 %s full empty\n", ptr)); } else { trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers 0 %d full empty\n", (int)trap_Cvar_VariableValue( "protocol" ) ) ); } } } openarena_0.8.8.orig/code/client/0000755000175000017500000000000011720470203015406 5ustar smcvsmcvopenarena_0.8.8.orig/code/client/keycodes.h0000644000175000017500000001011311656310265017372 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifndef __KEYCODES_H__ #define __KEYCODES_H__ // // these are the key numbers that should be passed to KeyEvent // // normal keys should be passed as lowercased ascii typedef enum { K_TAB = 9, K_ENTER = 13, K_ESCAPE = 27, K_SPACE = 32, K_BACKSPACE = 127, K_COMMAND = 128, K_CAPSLOCK, K_POWER, K_PAUSE, K_UPARROW, K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW, K_ALT, K_CTRL, K_SHIFT, K_INS, K_DEL, K_PGDN, K_PGUP, K_HOME, K_END, K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, K_F11, K_F12, K_F13, K_F14, K_F15, K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, K_KP_LEFTARROW, K_KP_5, K_KP_RIGHTARROW, K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_ENTER, K_KP_INS, K_KP_DEL, K_KP_SLASH, K_KP_MINUS, K_KP_PLUS, K_KP_NUMLOCK, K_KP_STAR, K_KP_EQUALS, K_MOUSE1, K_MOUSE2, K_MOUSE3, K_MOUSE4, K_MOUSE5, K_MWHEELDOWN, K_MWHEELUP, K_JOY1, K_JOY2, K_JOY3, K_JOY4, K_JOY5, K_JOY6, K_JOY7, K_JOY8, K_JOY9, K_JOY10, K_JOY11, K_JOY12, K_JOY13, K_JOY14, K_JOY15, K_JOY16, K_JOY17, K_JOY18, K_JOY19, K_JOY20, K_JOY21, K_JOY22, K_JOY23, K_JOY24, K_JOY25, K_JOY26, K_JOY27, K_JOY28, K_JOY29, K_JOY30, K_JOY31, K_JOY32, K_AUX1, K_AUX2, K_AUX3, K_AUX4, K_AUX5, K_AUX6, K_AUX7, K_AUX8, K_AUX9, K_AUX10, K_AUX11, K_AUX12, K_AUX13, K_AUX14, K_AUX15, K_AUX16, K_WORLD_0, K_WORLD_1, K_WORLD_2, K_WORLD_3, K_WORLD_4, K_WORLD_5, K_WORLD_6, K_WORLD_7, K_WORLD_8, K_WORLD_9, K_WORLD_10, K_WORLD_11, K_WORLD_12, K_WORLD_13, K_WORLD_14, K_WORLD_15, K_WORLD_16, K_WORLD_17, K_WORLD_18, K_WORLD_19, K_WORLD_20, K_WORLD_21, K_WORLD_22, K_WORLD_23, K_WORLD_24, K_WORLD_25, K_WORLD_26, K_WORLD_27, K_WORLD_28, K_WORLD_29, K_WORLD_30, K_WORLD_31, K_WORLD_32, K_WORLD_33, K_WORLD_34, K_WORLD_35, K_WORLD_36, K_WORLD_37, K_WORLD_38, K_WORLD_39, K_WORLD_40, K_WORLD_41, K_WORLD_42, K_WORLD_43, K_WORLD_44, K_WORLD_45, K_WORLD_46, K_WORLD_47, K_WORLD_48, K_WORLD_49, K_WORLD_50, K_WORLD_51, K_WORLD_52, K_WORLD_53, K_WORLD_54, K_WORLD_55, K_WORLD_56, K_WORLD_57, K_WORLD_58, K_WORLD_59, K_WORLD_60, K_WORLD_61, K_WORLD_62, K_WORLD_63, K_WORLD_64, K_WORLD_65, K_WORLD_66, K_WORLD_67, K_WORLD_68, K_WORLD_69, K_WORLD_70, K_WORLD_71, K_WORLD_72, K_WORLD_73, K_WORLD_74, K_WORLD_75, K_WORLD_76, K_WORLD_77, K_WORLD_78, K_WORLD_79, K_WORLD_80, K_WORLD_81, K_WORLD_82, K_WORLD_83, K_WORLD_84, K_WORLD_85, K_WORLD_86, K_WORLD_87, K_WORLD_88, K_WORLD_89, K_WORLD_90, K_WORLD_91, K_WORLD_92, K_WORLD_93, K_WORLD_94, K_WORLD_95, K_SUPER, K_COMPOSE, K_MODE, K_HELP, K_PRINT, K_SYSREQ, K_SCROLLOCK, K_BREAK, K_MENU, K_EURO, K_UNDO, MAX_KEYS } keyNum_t; // MAX_KEYS replaces K_LAST_KEY, however some mods may have used K_LAST_KEY // in detecting binds, so we leave it defined to the old hardcoded value // of maxiumum keys to prevent mods from crashing older versions of the engine #define K_LAST_KEY 256 // The menu code needs to get both key and char events, but // to avoid duplicating the paths, the char events are just // distinguished by or'ing in K_CHAR_FLAG (ugly) #define K_CHAR_FLAG 1024 #endif openarena_0.8.8.orig/code/renderer/0000755000175000017500000000000011720470203015736 5ustar smcvsmcvopenarena_0.8.8.orig/code/renderer/tr_types.h0000644000175000017500000001551011656310263017771 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifndef __TR_TYPES_H #define __TR_TYPES_H #define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces #define MAX_ENTITIES 1023 // can't be increased without changing drawsurf bit packing // renderfx flags #define RF_MINLIGHT 0x0001 // allways have some light (viewmodel, some items) #define RF_THIRD_PERSON 0x0002 // don't draw through eyes, only mirrors (player bodies, chat sprites) #define RF_FIRST_PERSON 0x0004 // only draw through eyes (view weapon, damage blood blob) #define RF_DEPTHHACK 0x0008 // for view weapon Z crunching #define RF_CROSSHAIR 0x0010 // This item is a cross hair and will draw over everything similar to // DEPTHHACK in stereo rendering mode, with the difference that the // projection matrix won't be hacked to reduce the stereo separation as // is done for the gun. #define RF_NOSHADOW 0x0040 // don't add stencil shadows #define RF_LIGHTING_ORIGIN 0x0080 // use refEntity->lightingOrigin instead of refEntity->origin // for lighting. This allows entities to sink into the floor // with their origin going solid, and allows all parts of a // player to get the same lighting #define RF_SHADOW_PLANE 0x0100 // use refEntity->shadowPlane #define RF_WRAP_FRAMES 0x0200 // mod the model frames by the maxframes to allow continuous // refdef flags #define RDF_NOWORLDMODEL 0x0001 // used for player configuration screen #define RDF_HYPERSPACE 0x0004 // teleportation effect typedef struct { vec3_t xyz; float st[2]; byte modulate[4]; } polyVert_t; typedef struct poly_s { qhandle_t hShader; int numVerts; polyVert_t *verts; } poly_t; typedef enum { RT_MODEL, RT_POLY, RT_SPRITE, RT_BEAM, RT_RAIL_CORE, RT_RAIL_RINGS, RT_LIGHTNING, RT_PORTALSURFACE, // doesn't draw anything, just info for portals RT_MAX_REF_ENTITY_TYPE } refEntityType_t; typedef struct { refEntityType_t reType; int renderfx; qhandle_t hModel; // opaque type outside refresh // most recent data vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) float shadowPlane; // projection shadows go here, stencils go slightly lower vec3_t axis[3]; // rotation vectors qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale float origin[3]; // also used as MODEL_BEAM's "from" int frame; // also used as MODEL_BEAM's diameter // previous data for frame interpolation float oldorigin[3]; // also used as MODEL_BEAM's "to" int oldframe; float backlerp; // 0.0 = current, 1.0 = old // texturing int skinNum; // inline skin index qhandle_t customSkin; // NULL for default skin qhandle_t customShader; // use one image for the entire thing // misc byte shaderRGBA[4]; // colors used by rgbgen entity shaders float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers float shaderTime; // subtracted from refdef time to control effect start times // extra sprite information float radius; float rotation; } refEntity_t; #define MAX_RENDER_STRINGS 8 #define MAX_RENDER_STRING_LENGTH 32 typedef struct { int x, y, width, height; float fov_x, fov_y; vec3_t vieworg; vec3_t viewaxis[3]; // transformation matrix // time in milliseconds for shader effects and other time dependent rendering issues int time; int rdflags; // RDF_NOWORLDMODEL, etc // 1 bits will prevent the associated area from rendering at all byte areamask[MAX_MAP_AREA_BYTES]; // text messages for deform text shaders char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; } refdef_t; typedef enum { STEREO_CENTER, STEREO_LEFT, STEREO_RIGHT } stereoFrame_t; /* ** glconfig_t ** ** Contains variables specific to the OpenGL configuration ** being run right now. These are constant once the OpenGL ** subsystem is initialized. */ typedef enum { TC_NONE, TC_S3TC, // this is for the GL_S3_s3tc extension. TC_S3TC_ARB // this is for the GL_EXT_texture_compression_s3tc extension. } textureCompression_t; typedef enum { GLDRV_ICD, // driver is integrated with window system // WARNING: there are tests that check for // > GLDRV_ICD for minidriverness, so this // should always be the lowest value in this // enum set GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver GLDRV_VOODOO // driver is a 3Dfx standalone driver } glDriverType_t; typedef enum { GLHW_GENERIC, // where everthing works the way it should GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is // the hardware type then there can NOT exist a secondary // display adapter GLHW_RIVA128, // where you can't interpolate alpha GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures GLHW_PERMEDIA2 // where you don't have src*dst } glHardwareType_t; typedef struct { char renderer_string[MAX_STRING_CHARS]; char vendor_string[MAX_STRING_CHARS]; char version_string[MAX_STRING_CHARS]; char extensions_string[BIG_INFO_STRING]; int maxTextureSize; // queried from GL int numTextureUnits; // multitexture ability int colorBits, depthBits, stencilBits; glDriverType_t driverType; glHardwareType_t hardwareType; qboolean deviceSupportsGamma; textureCompression_t textureCompression; qboolean textureEnvAddAvailable; int vidWidth, vidHeight; // aspect is the screen's physical width / height, which may be different // than scrWidth / scrHeight if the pixels are non-square // normal screens should be 4/3, but wide aspect monitors may be 16/9 float windowAspect; int displayFrequency; // synonymous with "does rendering consume the entire screen?", therefore // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that // used CDS. qboolean isFullscreen; qboolean stereoEnabled; qboolean smpActive; // dual processor } glconfig_t; #endif // __TR_TYPES_H openarena_0.8.8.orig/code/cgame/0000755000175000017500000000000011720470203015204 5ustar smcvsmcvopenarena_0.8.8.orig/code/cgame/cg_predict.c0000644000175000017500000006510611656310265017474 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_predict.c -- this file generates cg.predictedPlayerState by either // interpolating between snapshots from the server or locally predicting // ahead the client's movement. // It also handles local physics interaction, like fragments bouncing off walls #include "cg_local.h" static pmove_t cg_pmove; static int cg_numSolidEntities; static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT]; static int cg_numTriggerEntities; static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT]; /* ==================== CG_BuildSolidList When a new cg.snap has been set, this function builds a sublist of the entities that are actually solid, to make for more efficient collision detection ==================== */ void CG_BuildSolidList( void ) { int i; centity_t *cent; snapshot_t *snap; entityState_t *ent; cg_numSolidEntities = 0; cg_numTriggerEntities = 0; if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { snap = cg.nextSnap; } else { snap = cg.snap; } for ( i = 0 ; i < snap->numEntities ; i++ ) { cent = &cg_entities[ snap->entities[ i ].number ]; ent = ¢->currentState; if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) { cg_triggerEntities[cg_numTriggerEntities] = cent; cg_numTriggerEntities++; continue; } if ( cent->nextState.solid ) { cg_solidEntities[cg_numSolidEntities] = cent; cg_numSolidEntities++; continue; } } } /* ==================== CG_ClipMoveToEntities ==================== */ static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask, trace_t *tr ) { int i, x, zd, zu; trace_t trace; entityState_t *ent; clipHandle_t cmodel; vec3_t bmins, bmaxs; vec3_t origin, angles; centity_t *cent; for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { cent = cg_solidEntities[ i ]; ent = ¢->currentState; if ( ent->number == skipNumber ) { continue; } if ( ent->solid == SOLID_BMODEL ) { // special value for bmodel cmodel = trap_CM_InlineModel( ent->modelindex ); VectorCopy( cent->lerpAngles, angles ); BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin ); } else { // encoded bbox x = (ent->solid & 255); zd = ((ent->solid>>8) & 255); zu = ((ent->solid>>16) & 255) - 32; bmins[0] = bmins[1] = -x; bmaxs[0] = bmaxs[1] = x; bmins[2] = -zd; bmaxs[2] = zu; cmodel = trap_CM_TempBoxModel( bmins, bmaxs ); VectorCopy( vec3_origin, angles ); VectorCopy( cent->lerpOrigin, origin ); } trap_CM_TransformedBoxTrace ( &trace, start, end, mins, maxs, cmodel, mask, origin, angles); if (trace.allsolid || trace.fraction < tr->fraction) { trace.entityNum = ent->number; *tr = trace; } else if (trace.startsolid) { tr->startsolid = qtrue; } if ( tr->allsolid ) { return; } } } /* ================ CG_Trace ================ */ void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { trace_t t; trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask); t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; // check all other solid models CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t); *result = t; } /* ================ CG_PointContents ================ */ int CG_PointContents( const vec3_t point, int passEntityNum ) { int i; entityState_t *ent; centity_t *cent; clipHandle_t cmodel; int contents; contents = trap_CM_PointContents (point, 0); for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { cent = cg_solidEntities[ i ]; ent = ¢->currentState; if ( ent->number == passEntityNum ) { continue; } if (ent->solid != SOLID_BMODEL) { // special value for bmodel continue; } cmodel = trap_CM_InlineModel( ent->modelindex ); if ( !cmodel ) { continue; } contents |= trap_CM_TransformedPointContents( point, cmodel, cent->lerpOrigin, cent->lerpAngles ); } return contents; } /* ======================== CG_InterpolatePlayerState Generates cg.predictedPlayerState by interpolating between cg.snap->player_state and cg.nextFrame->player_state ======================== */ static void CG_InterpolatePlayerState( qboolean grabAngles ) { float f; int i; playerState_t *out; snapshot_t *prev, *next; out = &cg.predictedPlayerState; prev = cg.snap; next = cg.nextSnap; *out = cg.snap->ps; // if we are still allowing local input, short circuit the view angles if ( grabAngles ) { usercmd_t cmd; int cmdNum; cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); PM_UpdateViewAngles( out, &cmd ); } // if the next frame is a teleport, we can't lerp to it if ( cg.nextFrameTeleport ) { return; } if ( !next || next->serverTime <= prev->serverTime ) { return; } f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); i = next->ps.bobCycle; if ( i < prev->ps.bobCycle ) { i += 256; // handle wraparound } out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); for ( i = 0 ; i < 3 ; i++ ) { out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] ); if ( !grabAngles ) { out->viewangles[i] = LerpAngle( prev->ps.viewangles[i], next->ps.viewangles[i], f ); } out->velocity[i] = prev->ps.velocity[i] + f * (next->ps.velocity[i] - prev->ps.velocity[i] ); } } /* =================== CG_TouchItem =================== */ static void CG_TouchItem( centity_t *cent ) { gitem_t *item; //For instantgib qboolean canBePicked; if(cgs.gametype == GT_ELIMINATION || cgs.gametype == GT_LMS) return; //No weapon pickup in elimination //normally we can canBePicked = qtrue; //But in instantgib, rocket arena, and CTF_ELIMINATION we normally can't: if(cgs.nopickup || cgs.gametype == GT_CTF_ELIMINATION) canBePicked = qfalse; if ( !cg_predictItems.integer ) { return; } if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) { return; } // never pick an item up twice in a prediction if ( cent->miscTime == cg.time ) { return; } if ( !BG_CanItemBeGrabbed( cgs.gametype, ¢->currentState, &cg.predictedPlayerState ) ) { return; // can't hold it } item = &bg_itemlist[ cent->currentState.modelindex ]; // Special case for flags. // We don't predict touching our own flag #if 1 //MISSIONPACK if( cgs.gametype == GT_1FCTF ) { if( item->giTag != PW_NEUTRALFLAG ) { return; } } if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_HARVESTER ) { #else if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION ) { #endif if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED && item->giTag == PW_REDFLAG) return; if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE && item->giTag == PW_BLUEFLAG) return; //Even in instantgib, we can predict our enemy flag if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED && item->giTag == PW_BLUEFLAG && (!(cgs.elimflags&EF_ONEWAY) || cgs.attackingTeam == TEAM_RED)) canBePicked = qtrue; if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE && item->giTag == PW_REDFLAG && (!(cgs.elimflags&EF_ONEWAY) || cgs.attackingTeam == TEAM_BLUE)) canBePicked = qtrue; if (item->giTag == WP_RAILGUN) canBePicked = qfalse; if (item->giTag == WP_PLASMAGUN) canBePicked = qfalse; } //Currently we don't predict anything in Double Domination because it looks like we take a flag if( cgs.gametype == GT_DOUBLE_D ) { if(cgs.redflag == TEAM_NONE) return; //Can never pick if just one flag is NONE (because then the other is too) if(item->giTag == PW_REDFLAG){ //at point A if(cgs.redflag != cg.predictedPlayerState.persistant[PERS_TEAM]) //not already taken trap_S_StartLocalSound( cgs.media.hitSound , CHAN_ANNOUNCER ); return; } if(item->giTag == PW_BLUEFLAG){ //at point B if(cgs.blueflag != cg.predictedPlayerState.persistant[PERS_TEAM]) //already taken trap_S_StartLocalSound( cgs.media.hitSound , CHAN_ANNOUNCER ); return; } } // grab it if(canBePicked) { BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState); // remove it from the frame so it won't be drawn cent->currentState.eFlags |= EF_NODRAW; // don't touch it again this prediction cent->miscTime = cg.time; // if its a weapon, give them some predicted ammo so the autoswitch will work if ( item->giType == IT_WEAPON ) { cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag; if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) { cg.predictedPlayerState.ammo[ item->giTag ] = 1; } } } } /* ========================= CG_TouchTriggerPrediction Predict push triggers and items ========================= */ static void CG_TouchTriggerPrediction( void ) { int i; trace_t trace; entityState_t *ent; clipHandle_t cmodel; centity_t *cent; qboolean spectator; // dead clients don't activate triggers if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return; } spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR ); if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) { return; } for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) { cent = cg_triggerEntities[ i ]; ent = ¢->currentState; if ( ent->eType == ET_ITEM && !spectator ) { CG_TouchItem( cent ); continue; } if ( ent->solid != SOLID_BMODEL ) { continue; } cmodel = trap_CM_InlineModel( ent->modelindex ); if ( !cmodel ) { continue; } trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin, cg_pmove.mins, cg_pmove.maxs, cmodel, -1 ); if ( !trace.startsolid ) { continue; } if ( ent->eType == ET_TELEPORT_TRIGGER ) { cg.hyperspace = qtrue; } else if ( ent->eType == ET_PUSH_TRIGGER ) { BG_TouchJumpPad( &cg.predictedPlayerState, ent ); } } // if we didn't touch a jump pad this pmove frame if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) { cg.predictedPlayerState.jumppad_frame = 0; cg.predictedPlayerState.jumppad_ent = 0; } } //unlagged - optimized prediction #define ABS(x) ((x) < 0 ? (-(x)) : (x)) static int IsUnacceptableError( playerState_t *ps, playerState_t *pps ) { vec3_t delta; int i; if ( pps->pm_type != ps->pm_type || pps->pm_flags != ps->pm_flags || pps->pm_time != ps->pm_time ) { return 1; } VectorSubtract( pps->origin, ps->origin, delta ); if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) { if ( cg_showmiss.integer ) { CG_Printf("delta: %.2f ", VectorLength(delta) ); } return 2; } VectorSubtract( pps->velocity, ps->velocity, delta ); if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) { if ( cg_showmiss.integer ) { CG_Printf("delta: %.2f ", VectorLength(delta) ); } return 3; } if ( pps->weaponTime != ps->weaponTime || pps->gravity != ps->gravity || pps->speed != ps->speed || pps->delta_angles[0] != ps->delta_angles[0] || pps->delta_angles[1] != ps->delta_angles[1] || pps->delta_angles[2] != ps->delta_angles[2] || pps->groundEntityNum != ps->groundEntityNum ) { return 4; } if ( pps->legsTimer != ps->legsTimer || pps->legsAnim != ps->legsAnim || pps->torsoTimer != ps->torsoTimer || pps->torsoAnim != ps->torsoAnim || pps->movementDir != ps->movementDir ) { return 5; } VectorSubtract( pps->grapplePoint, ps->grapplePoint, delta ); if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) { return 6; } if ( pps->eFlags != ps->eFlags ) { return 7; } if ( pps->eventSequence != ps->eventSequence ) { return 8; } for ( i = 0; i < MAX_PS_EVENTS; i++ ) { if ( pps->events[i] != ps->events[i] || pps->eventParms[i] != ps->eventParms[i] ) { return 9; } } if ( pps->externalEvent != ps->externalEvent || pps->externalEventParm != ps->externalEventParm || pps->externalEventTime != ps->externalEventTime ) { return 10; } if ( pps->clientNum != ps->clientNum || pps->weapon != ps->weapon || pps->weaponstate != ps->weaponstate ) { return 11; } if ( ABS(pps->viewangles[0] - ps->viewangles[0]) > 1.0f || ABS(pps->viewangles[1] - ps->viewangles[1]) > 1.0f || ABS(pps->viewangles[2] - ps->viewangles[2]) > 1.0f ) { return 12; } if ( pps->viewheight != ps->viewheight ) { return 13; } if ( pps->damageEvent != ps->damageEvent || pps->damageYaw != ps->damageYaw || pps->damagePitch != ps->damagePitch || pps->damageCount != ps->damageCount ) { return 14; } for ( i = 0; i < MAX_STATS; i++ ) { if ( pps->stats[i] != ps->stats[i] ) { return 15; } } for ( i = 0; i < MAX_PERSISTANT; i++ ) { if ( pps->persistant[i] != ps->persistant[i] ) { return 16; } } for ( i = 0; i < MAX_POWERUPS; i++ ) { if ( pps->powerups[i] != ps->powerups[i] ) { return 17; } } for ( i = 0; i < MAX_WEAPONS; i++ ) { if ( pps->ammo[i] != ps->ammo[i] ) { return 18; } } if ( pps->generic1 != ps->generic1 || pps->loopSound != ps->loopSound || pps->jumppad_ent != ps->jumppad_ent ) { return 19; } return 0; } //unlagged - optimized prediction /* ================= CG_PredictPlayerState Generates cg.predictedPlayerState for the current cg.time cg.predictedPlayerState is guaranteed to be valid after exiting. For demo playback, this will be an interpolation between two valid playerState_t. For normal gameplay, it will be the result of predicted usercmd_t on top of the most recent playerState_t received from the server. Each new snapshot will usually have one or more new usercmd over the last, but we simulate all unacknowledged commands each time, not just the new ones. This means that on an internet connection, quite a few pmoves may be issued each frame. OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t differs from the predicted one. Would require saving all intermediate playerState_t during prediction. We detect prediction errors and allow them to be decayed off over several frames to ease the jerk. ================= */ void CG_PredictPlayerState( void ) { int cmdNum, current; playerState_t oldPlayerState; qboolean moved; usercmd_t oldestCmd; usercmd_t latestCmd; //unlagged - optimized prediction int stateIndex = 0, predictCmd = 0; //Sago: added initializing int numPredicted = 0, numPlayedBack = 0; // debug code //unlagged - optimized prediction cg.hyperspace = qfalse; // will be set if touching a trigger_teleport // if this is the first frame we must guarantee // predictedPlayerState is valid even if there is some // other error condition if ( !cg.validPPS ) { cg.validPPS = qtrue; cg.predictedPlayerState = cg.snap->ps; } // demo playback just copies the moves if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) { CG_InterpolatePlayerState( qfalse ); return; } // non-predicting local movement will grab the latest angles if ( cg_nopredict.integer || cg_synchronousClients.integer ) { CG_InterpolatePlayerState( qtrue ); return; } // prepare for pmove cg_pmove.ps = &cg.predictedPlayerState; cg_pmove.trace = CG_Trace; cg_pmove.pointcontents = CG_PointContents; if ( cg_pmove.ps->pm_type == PM_DEAD ) { cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else { cg_pmove.tracemask = MASK_PLAYERSOLID; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies } cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0; // save the state before the pmove so we can detect transitions oldPlayerState = cg.predictedPlayerState; current = trap_GetCurrentCmdNumber(); // if we don't have the commands right after the snapshot, we // can't accurately predict a current position, so just freeze at // the last good position we had cmdNum = current - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &oldestCmd ); if ( oldestCmd.serverTime > cg.snap->ps.commandTime && oldestCmd.serverTime < cg.time ) { // special check for map_restart if ( cg_showmiss.integer ) { CG_Printf ("exceeded PACKET_BACKUP on commands\n"); } return; } // get the latest command so we can know which commands are from previous map_restarts trap_GetUserCmd( current, &latestCmd ); // get the most recent information we have, even if // the server time is beyond our current cg.time, // because predicted player positions are going to // be ahead of everything else anyway if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { cg.predictedPlayerState = cg.nextSnap->ps; cg.physicsTime = cg.nextSnap->serverTime; } else { cg.predictedPlayerState = cg.snap->ps; cg.physicsTime = cg.snap->serverTime; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer; cg_pmove.pmove_msec = pmove_msec.integer; cg_pmove.pmove_float = pmove_float.integer; cg_pmove.pmove_flags = cgs.dmflags; //unlagged - optimized prediction // Like the comments described above, a player's state is entirely // re-predicted from the last valid snapshot every client frame, which // can be really, really, really slow. Every old command has to be // run again. For every client frame that is *not* directly after a // snapshot, this is unnecessary, since we have no new information. // For those, we'll play back the predictions from the last frame and // predict only the newest commands. Essentially, we'll be doing // an incremental predict instead of a full predict. // // If we have a new snapshot, we can compare its player state's command // time to the command times in the queue to find a match. If we find // a matching state, and the predicted version has not deviated, we can // use the predicted state as a base - and also do an incremental predict. // // With this method, we get incremental predicts on every client frame // except a frame following a new snapshot in which there was a prediction // error. This yeilds anywhere from a 15% to 40% performance increase, // depending on how much of a bottleneck the CPU is. if ( cg_optimizePrediction.integer ) { if ( cg.nextFrameTeleport || cg.thisFrameTeleport ) { // do a full predict cg.lastPredictedCommand = 0; cg.stateTail = cg.stateHead; predictCmd = current - CMD_BACKUP + 1; } // cg.physicsTime is the current snapshot's serverTime // if it's the same as the last one else if ( cg.physicsTime == cg.lastServerTime ) { // we have no new information, so do an incremental predict predictCmd = cg.lastPredictedCommand + 1; } else { // we have a new snapshot int i; qboolean error = qtrue; // loop through the saved states queue for ( i = cg.stateHead; i != cg.stateTail; i = (i + 1) % NUM_SAVED_STATES ) { // if we find a predicted state whose commandTime matches the snapshot player state's commandTime if ( cg.savedPmoveStates[i].commandTime == cg.predictedPlayerState.commandTime ) { // make sure the state differences are acceptable int errorcode = IsUnacceptableError( &cg.predictedPlayerState, &cg.savedPmoveStates[i] ); // too much change? if ( errorcode ) { if ( cg_showmiss.integer ) { CG_Printf("errorcode %d at %d\n", errorcode, cg.time); } // yeah, so do a full predict break; } // this one is almost exact, so we'll copy it in as the starting point *cg_pmove.ps = cg.savedPmoveStates[i]; // advance the head cg.stateHead = (i + 1) % NUM_SAVED_STATES; // set the next command to predict predictCmd = cg.lastPredictedCommand + 1; // a saved state matched, so flag it error = qfalse; break; } } // if no saved states matched if ( error ) { // do a full predict cg.lastPredictedCommand = 0; cg.stateTail = cg.stateHead; predictCmd = current - CMD_BACKUP + 1; } } // keep track of the server time of the last snapshot so we // know when we're starting from a new one in future calls cg.lastServerTime = cg.physicsTime; stateIndex = cg.stateHead; } //unlagged - optimized prediction // run cmds moved = qfalse; for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) { // get the command trap_GetUserCmd( cmdNum, &cg_pmove.cmd ); if ( cg_pmove.pmove_fixed ) { PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd ); } // don't do anything if the time is before the snapshot player time if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) { continue; } // don't do anything if the command was from a previous map_restart if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) { continue; } // check for a prediction error from last frame // on a lan, this will often be the exact value // from the snapshot, but on a wan we will have // to predict several commands to get to the point // we want to compare if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) { vec3_t delta; float len; if ( cg.thisFrameTeleport ) { // a teleport will not cause an error decay VectorClear( cg.predictedError ); if ( cg_showmiss.integer ) { CG_Printf( "PredictionTeleport\n" ); } cg.thisFrameTeleport = qfalse; } else { vec3_t adjusted; CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted ); if ( cg_showmiss.integer ) { if (!VectorCompare( oldPlayerState.origin, adjusted )) { CG_Printf("prediction error\n"); } } VectorSubtract( oldPlayerState.origin, adjusted, delta ); len = VectorLength( delta ); if ( len > 0.1 ) { if ( cg_showmiss.integer ) { CG_Printf("Prediction miss: %f\n", len); } if ( cg_errorDecay.integer ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f < 0 ) { f = 0; } if ( f > 0 && cg_showmiss.integer ) { CG_Printf("Double prediction decay: %f\n", f); } VectorScale( cg.predictedError, f, cg.predictedError ); } else { VectorClear( cg.predictedError ); } VectorAdd( delta, cg.predictedError, cg.predictedError ); cg.predictedErrorTime = cg.oldTime; } } } // don't predict gauntlet firing, which is only supposed to happen // when it actually inflicts damage cg_pmove.gauntletHit = qfalse; if ( cg_pmove.pmove_fixed ) { cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; } //unlagged - optimized prediction if ( cg_optimizePrediction.integer ) { // if we need to predict this command, or we've run out of space in the saved states queue if ( cmdNum >= predictCmd || (stateIndex + 1) % NUM_SAVED_STATES == cg.stateHead ) { // run the Pmove Pmove (&cg_pmove); numPredicted++; // debug code // record the last predicted command cg.lastPredictedCommand = cmdNum; // if we haven't run out of space in the saved states queue if ( (stateIndex + 1) % NUM_SAVED_STATES != cg.stateHead ) { // save the state for the false case (of cmdNum >= predictCmd) // in later calls to this function cg.savedPmoveStates[stateIndex] = *cg_pmove.ps; stateIndex = (stateIndex + 1) % NUM_SAVED_STATES; cg.stateTail = stateIndex; } } else { numPlayedBack++; // debug code if ( cg_showmiss.integer && cg.savedPmoveStates[stateIndex].commandTime != cg_pmove.cmd.serverTime) { // this should ONLY happen just after changing the value of pmove_fixed CG_Printf( "saved state miss\n" ); } // play back the command from the saved states *cg_pmove.ps = cg.savedPmoveStates[stateIndex]; // go to the next element in the saved states array stateIndex = (stateIndex + 1) % NUM_SAVED_STATES; } } else { // run the Pmove Pmove (&cg_pmove); numPredicted++; // debug code } //unlagged - optimized prediction moved = qtrue; // add push trigger movement effects CG_TouchTriggerPrediction(); // check for predictable events that changed from previous predictions //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState); } //unlagged - optimized prediction // do a /condump after a few seconds of this //CG_Printf("cg.time: %d, numPredicted: %d, numPlayedBack: %d\n", cg.time, numPredicted, numPlayedBack); // debug code // if everything is working right, numPredicted should be 1 more than 98% // of the time, meaning only ONE predicted move was done in the frame // you should see other values for numPredicted after IsUnacceptableError // returns nonzero, and that's it //unlagged - optimized prediction if ( cg_showmiss.integer > 1 ) { CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time ); } if ( !moved ) { if ( cg_showmiss.integer ) { CG_Printf( "not moved\n" ); } return; } // adjust for the movement of the groundentity CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.time, cg.predictedPlayerState.origin ); if ( cg_showmiss.integer ) { if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) { CG_Printf("WARNING: dropped event\n"); } } // fire events and other transition triggered things CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState ); if ( cg_showmiss.integer ) { if (cg.eventSequence > cg.predictedPlayerState.eventSequence) { CG_Printf("WARNING: double event\n"); cg.eventSequence = cg.predictedPlayerState.eventSequence; } } } openarena_0.8.8.orig/code/cgame/cg_unlagged.c0000644000175000017500000003520311656310265017623 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2006 Neil Toronto. This file is part of the Unlagged source code. Unlagged source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Unlagged source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Unlagged source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //#include "cg_local.h" #include "cg_local.h" // we'll need these prototypes void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum ); void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ); // and this as well //Must be in sync with g_weapon.c #define MACHINEGUN_SPREAD 200 #define CHAINGUN_SPREAD 600 /* ======================= CG_PredictWeaponEffects Draws predicted effects for the railgun, shotgun, and machinegun. The lightning gun is done in CG_LightningBolt, since it was just a matter of setting the right origin and angles. ======================= */ void CG_PredictWeaponEffects( centity_t *cent ) { vec3_t muzzlePoint, forward, right, up; entityState_t *ent = ¢->currentState; // if the client isn't us, forget it if ( cent->currentState.number != cg.predictedPlayerState.clientNum ) { return; } // if it's not switched on server-side, forget it if ( !cgs.delagHitscan ) { return; } // get the muzzle point VectorCopy( cg.predictedPlayerState.origin, muzzlePoint ); muzzlePoint[2] += cg.predictedPlayerState.viewheight; // get forward, right, and up AngleVectors( cg.predictedPlayerState.viewangles, forward, right, up ); VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // was it a rail attack? if ( ent->weapon == WP_RAILGUN ) { // do we have it on for the rail gun? if ( cg_delag.integer & 1 || cg_delag.integer & 16 ) { trace_t trace; vec3_t endPoint; // trace forward VectorMA( muzzlePoint, 8192, forward, endPoint ); // THIS IS FOR DEBUGGING! // you definitely *will* want something like this to test the backward reconciliation // to make sure it's working *exactly* right /*if ( cg_debugDelag.integer ) { * Sago: There are some problems with some unlagged code. People will just have to trust it // trace forward CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, CONTENTS_BODY|CONTENTS_SOLID ); // did we hit another player? if ( trace.fraction < 1.0f && (trace.contents & CONTENTS_BODY) ) { // if we have two snapshots (we're interpolating) if ( cg.nextSnap ) { centity_t *c = &cg_entities[trace.entityNum]; vec3_t origin1, origin2; // figure the two origins used for interpolation BG_EvaluateTrajectory( &c->currentState.pos, cg.snap->serverTime, origin1 ); BG_EvaluateTrajectory( &c->nextState.pos, cg.nextSnap->serverTime, origin2 ); // print some debugging stuff exactly like what the server does // it starts with "Int:" to let you know the target was interpolated CG_Printf("^3Int: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n", cg.oldTime, cg.snap->serverTime, cg.nextSnap->serverTime, c->lerpOrigin[0], c->lerpOrigin[1], c->lerpOrigin[2]); CG_Printf("^5frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n", cg.frameInterpolation, origin1[0], origin1[1], origin1[2], origin2[0], origin2[1], origin2[2]); } else { // we haven't got a next snapshot // the client clock has either drifted ahead (seems to happen once per server frame // when you play locally) or the client is using timenudge // in any case, CG_CalcEntityLerpPositions extrapolated rather than interpolated centity_t *c = &cg_entities[trace.entityNum]; vec3_t origin1, origin2; c->currentState.pos.trTime = TR_LINEAR_STOP; c->currentState.pos.trTime = cg.snap->serverTime; c->currentState.pos.trDuration = 1000 / sv_fps.integer; BG_EvaluateTrajectory( &c->currentState.pos, cg.snap->serverTime, origin1 ); BG_EvaluateTrajectory( &c->currentState.pos, cg.snap->serverTime + 1000 / sv_fps.integer, origin2 ); // print some debugging stuff exactly like what the server does // it starts with "Ext:" to let you know the target was extrapolated CG_Printf("^3Ext: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n", cg.oldTime, cg.snap->serverTime, cg.snap->serverTime, c->lerpOrigin[0], c->lerpOrigin[1], c->lerpOrigin[2]); CG_Printf("^5frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n", cg.frameInterpolation, origin1[0], origin1[1], origin1[2], origin2[0], origin2[1], origin2[2]); } } }*/ // find the rail's end point CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cg.predictedPlayerState.clientNum, CONTENTS_SOLID ); // do the magic-number adjustment VectorMA( muzzlePoint, 4, right, muzzlePoint ); VectorMA( muzzlePoint, -1, up, muzzlePoint ); if(!cg.renderingThirdPerson) { if(cg_drawGun.integer == 2) VectorMA(muzzlePoint, 8, cg.refdef.viewaxis[1], muzzlePoint); else if(cg_drawGun.integer == 3) VectorMA(muzzlePoint, 4, cg.refdef.viewaxis[1], muzzlePoint); } // draw a rail trail CG_RailTrail( &cgs.clientinfo[cent->currentState.number], muzzlePoint, trace.endpos ); //Com_Printf( "Predicted rail trail\n" ); // explosion at end if not SURF_NOIMPACT if ( !(trace.surfaceFlags & SURF_NOIMPACT) ) { // predict an explosion CG_MissileHitWall( ent->weapon, cg.predictedPlayerState.clientNum, trace.endpos, trace.plane.normal, IMPACTSOUND_DEFAULT ); } } } // was it a shotgun attack? else if ( ent->weapon == WP_SHOTGUN ) { // do we have it on for the shotgun? if ( cg_delag.integer & 1 || cg_delag.integer & 4 ) { int contents; vec3_t endPoint, v; // do everything like the server does SnapVector( muzzlePoint ); VectorScale( forward, 4096, endPoint ); SnapVector( endPoint ); VectorSubtract( endPoint, muzzlePoint, v ); VectorNormalize( v ); VectorScale( v, 32, v ); VectorAdd( muzzlePoint, v, v ); if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) { // ragepro can't alpha fade, so don't even bother with smoke vec3_t up; contents = trap_CM_PointContents( muzzlePoint, 0 ); if ( !( contents & CONTENTS_WATER ) ) { VectorSet( up, 0, 0, 8 ); CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33f, 900, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); } } // do the shotgun pellets CG_ShotgunPattern( muzzlePoint, endPoint, cg.oldTime % 256, cg.predictedPlayerState.clientNum ); //Com_Printf( "Predicted shotgun pattern\n" ); } } // was it a machinegun attack? else if ( ent->weapon == WP_MACHINEGUN ) { // do we have it on for the machinegun? if ( cg_delag.integer & 1 || cg_delag.integer & 2 ) { // the server will use this exact time (it'll be serverTime on that end) int seed = cg.oldTime % 256; float r, u; trace_t tr; qboolean flesh; int fleshEntityNum = 0; vec3_t endPoint; // do everything exactly like the server does r = Q_random(&seed) * M_PI * 2.0f; u = sin(r) * Q_crandom(&seed) * MACHINEGUN_SPREAD * 16; r = cos(r) * Q_crandom(&seed) * MACHINEGUN_SPREAD * 16; VectorMA( muzzlePoint, 8192*16, forward, endPoint ); VectorMA( endPoint, r, right, endPoint ); VectorMA( endPoint, u, up, endPoint ); CG_Trace(&tr, muzzlePoint, NULL, NULL, endPoint, cg.predictedPlayerState.clientNum, MASK_SHOT ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzlePoint ); // do bullet impact if ( tr.entityNum < MAX_CLIENTS ) { flesh = qtrue; fleshEntityNum = tr.entityNum; } else { flesh = qfalse; } // do the bullet impact CG_Bullet( tr.endpos, cg.predictedPlayerState.clientNum, tr.plane.normal, flesh, fleshEntityNum ); //Com_Printf( "Predicted bullet\n" ); } } // was it a chaingun attack? else if ( ent->weapon == WP_CHAINGUN ) { // do we have it on for the machinegun? if ( cg_delag.integer & 1 || cg_delag.integer & 2 ) { // the server will use this exact time (it'll be serverTime on that end) int seed = cg.oldTime % 256; float r, u; trace_t tr; qboolean flesh; int fleshEntityNum = 0; vec3_t endPoint; // do everything exactly like the server does r = Q_random(&seed) * M_PI * 2.0f; u = sin(r) * Q_crandom(&seed) * CHAINGUN_SPREAD * 16; r = cos(r) * Q_crandom(&seed) * CHAINGUN_SPREAD * 16; VectorMA( muzzlePoint, 8192*16, forward, endPoint ); VectorMA( endPoint, r, right, endPoint ); VectorMA( endPoint, u, up, endPoint ); CG_Trace(&tr, muzzlePoint, NULL, NULL, endPoint, cg.predictedPlayerState.clientNum, MASK_SHOT ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzlePoint ); // do bullet impact if ( tr.entityNum < MAX_CLIENTS ) { flesh = qtrue; fleshEntityNum = tr.entityNum; } else { flesh = qfalse; } // do the bullet impact CG_Bullet( tr.endpos, cg.predictedPlayerState.clientNum, tr.plane.normal, flesh, fleshEntityNum ); //Com_Printf( "Predicted bullet\n" ); } } } /* ================= CG_AddBoundingBox Draws a bounding box around a player. Called from CG_Player. ================= */ /*void CG_AddBoundingBox( centity_t *cent ) { polyVert_t verts[4]; clientInfo_t *ci; int i; vec3_t mins = {-15, -15, -24}; vec3_t maxs = {15, 15, 32}; float extx, exty, extz; vec3_t corners[8]; qhandle_t bboxShader, bboxShader_nocull; if ( !cg_drawBBox.integer ) { return; } // don't draw it if it's us in first-person if ( cent->currentState.number == cg.predictedPlayerState.clientNum && !cg.renderingThirdPerson ) { return; } // don't draw it for dead players if ( cent->currentState.eFlags & EF_DEAD ) { return; } // get the shader handles bboxShader = trap_R_RegisterShader( "bbox" ); bboxShader_nocull = trap_R_RegisterShader( "bbox_nocull" ); // if they don't exist, forget it if ( !bboxShader || !bboxShader_nocull ) { return; } // get the player's client info ci = &cgs.clientinfo[cent->currentState.clientNum]; // if it's us if ( cent->currentState.number == cg.predictedPlayerState.clientNum ) { // use the view height maxs[2] = cg.predictedPlayerState.viewheight + 6; } else { int x, zd, zu; // otherwise grab the encoded bounding box x = (cent->currentState.solid & 255); zd = ((cent->currentState.solid>>8) & 255); zu = ((cent->currentState.solid>>16) & 255) - 32; mins[0] = mins[1] = -x; maxs[0] = maxs[1] = x; mins[2] = -zd; maxs[2] = zu; } // get the extents (size) extx = maxs[0] - mins[0]; exty = maxs[1] - mins[1]; extz = maxs[2] - mins[2]; // set the polygon's texture coordinates verts[0].st[0] = 0; verts[0].st[1] = 0; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[3].st[0] = 1; verts[3].st[1] = 0; // set the polygon's vertex colors if ( ci->team == TEAM_RED ) { for ( i = 0; i < 4; i++ ) { verts[i].modulate[0] = 160; verts[i].modulate[1] = 0; verts[i].modulate[2] = 0; verts[i].modulate[3] = 255; } } else if ( ci->team == TEAM_BLUE ) { for ( i = 0; i < 4; i++ ) { verts[i].modulate[0] = 0; verts[i].modulate[1] = 0; verts[i].modulate[2] = 192; verts[i].modulate[3] = 255; } } else { for ( i = 0; i < 4; i++ ) { verts[i].modulate[0] = 0; verts[i].modulate[1] = 128; verts[i].modulate[2] = 0; verts[i].modulate[3] = 255; } } VectorAdd( cent->lerpOrigin, maxs, corners[3] ); VectorCopy( corners[3], corners[2] ); corners[2][0] -= extx; VectorCopy( corners[2], corners[1] ); corners[1][1] -= exty; VectorCopy( corners[1], corners[0] ); corners[0][0] += extx; for ( i = 0; i < 4; i++ ) { VectorCopy( corners[i], corners[i + 4] ); corners[i + 4][2] -= extz; } // top VectorCopy( corners[0], verts[0].xyz ); VectorCopy( corners[1], verts[1].xyz ); VectorCopy( corners[2], verts[2].xyz ); VectorCopy( corners[3], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader, 4, verts ); // bottom VectorCopy( corners[7], verts[0].xyz ); VectorCopy( corners[6], verts[1].xyz ); VectorCopy( corners[5], verts[2].xyz ); VectorCopy( corners[4], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader, 4, verts ); // top side VectorCopy( corners[3], verts[0].xyz ); VectorCopy( corners[2], verts[1].xyz ); VectorCopy( corners[6], verts[2].xyz ); VectorCopy( corners[7], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader_nocull, 4, verts ); // left side VectorCopy( corners[2], verts[0].xyz ); VectorCopy( corners[1], verts[1].xyz ); VectorCopy( corners[5], verts[2].xyz ); VectorCopy( corners[6], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader_nocull, 4, verts ); // right side VectorCopy( corners[0], verts[0].xyz ); VectorCopy( corners[3], verts[1].xyz ); VectorCopy( corners[7], verts[2].xyz ); VectorCopy( corners[4], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader_nocull, 4, verts ); // bottom side VectorCopy( corners[1], verts[0].xyz ); VectorCopy( corners[0], verts[1].xyz ); VectorCopy( corners[4], verts[2].xyz ); VectorCopy( corners[5], verts[3].xyz ); trap_R_AddPolyToScene( bboxShader_nocull, 4, verts ); }*/ /* ================ CG_Cvar_ClampInt Clamps a cvar between two integer values, returns qtrue if it had to. ================ */ qboolean CG_Cvar_ClampInt( const char *name, vmCvar_t *vmCvar, int min, int max ) { if ( vmCvar->integer > max ) { CG_Printf( "Allowed values are %d to %d.\n", min, max ); Com_sprintf( vmCvar->string, MAX_CVAR_VALUE_STRING, "%d", max ); vmCvar->value = max; vmCvar->integer = max; trap_Cvar_Set( name, vmCvar->string ); return qtrue; } if ( vmCvar->integer < min ) { CG_Printf( "Allowed values are %d to %d.\n", min, max ); Com_sprintf( vmCvar->string, MAX_CVAR_VALUE_STRING, "%d", min ); vmCvar->value = min; vmCvar->integer = min; trap_Cvar_Set( name, vmCvar->string ); return qtrue; } return qfalse; } openarena_0.8.8.orig/code/cgame/cg_effects.c0000644000175000017500000004673111656310265017464 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_effects.c -- these functions generate localentities, usually as a result // of event processing #include "cg_local.h" /* ================== CG_BubbleTrail Bullets shot underwater ================== */ void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) { vec3_t move; vec3_t vec; float len; int i; if ( cg_noProjectileTrail.integer ) { return; } VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); // advance a random amount first i = rand() % (int)spacing; VectorMA( move, i, vec, move ); VectorScale (vec, spacing, vec); for ( ; i < len; i += spacing ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = LEF_PUFF_DONT_SCALE; le->leType = LE_MOVE_SCALE_FADE; le->startTime = cg.time; le->endTime = cg.time + 1000 + random() * 250; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); re = &le->refEntity; re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->rotation = 0; re->radius = 3; re->customShader = cgs.media.waterBubbleShader; re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; re->shaderRGBA[3] = 0xff; le->color[3] = 1.0; le->pos.trType = TR_LINEAR; le->pos.trTime = cg.time; VectorCopy( move, le->pos.trBase ); le->pos.trDelta[0] = crandom()*5; le->pos.trDelta[1] = crandom()*5; le->pos.trDelta[2] = crandom()*5 + 6; VectorAdd (move, vec, move); } } /* ===================== CG_SmokePuff Adds a smoke puff or blood trail localEntity. ===================== */ localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, float radius, float r, float g, float b, float a, float duration, int startTime, int fadeInTime, int leFlags, qhandle_t hShader ) { static int seed = 0x92; localEntity_t *le; refEntity_t *re; // int fadeInTime = startTime + duration / 2; le = CG_AllocLocalEntity(); le->leFlags = leFlags; le->radius = radius; re = &le->refEntity; re->rotation = Q_random( &seed ) * 360; re->radius = radius; re->shaderTime = startTime / 1000.0f; le->leType = LE_MOVE_SCALE_FADE; le->startTime = startTime; le->fadeInTime = fadeInTime; le->endTime = startTime + duration; if ( fadeInTime > startTime ) { le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); } else { le->lifeRate = 1.0 / ( le->endTime - le->startTime ); } le->color[0] = r; le->color[1] = g; le->color[2] = b; le->color[3] = a; le->pos.trType = TR_LINEAR_STOP; le->pos.trTime = startTime; VectorCopy( vel, le->pos.trDelta ); VectorCopy( p, le->pos.trBase ); VectorCopy( p, re->origin ); re->customShader = hShader; // rage pro can't alpha fade, so use a different shader if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { re->customShader = cgs.media.smokePuffRageProShader; re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; re->shaderRGBA[3] = 0xff; } else { re->shaderRGBA[0] = le->color[0] * 0xff; re->shaderRGBA[1] = le->color[1] * 0xff; re->shaderRGBA[2] = le->color[2] * 0xff; re->shaderRGBA[3] = 0xff; } re->reType = RT_SPRITE; re->radius = le->radius; return le; } // LEILEI same as above, but slows down....... localEntity_t *CG_SlowPuff( const vec3_t p, const vec3_t vel, float radius, float r, float g, float b, float a, float duration, int startTime, int fadeInTime, int leFlags, qhandle_t hShader ) { static int seed = 0x92; localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = leFlags; le->radius = radius; re = &le->refEntity; re->rotation = Q_random( &seed ) * 360; re->radius = radius; re->shaderTime = startTime / 1000.0f; le->leType = LE_MOVE_SCALE_FADE; le->startTime = startTime; le->fadeInTime = fadeInTime; le->endTime = startTime + duration; if ( fadeInTime > startTime ) { le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); } else { le->lifeRate = 1.0 / ( le->endTime - le->startTime ); } le->color[0] = r; le->color[1] = g; le->color[2] = b; le->color[3] = a; le->pos.trType = TR_LINEAR; le->pos.trTime = startTime; VectorCopy( vel, le->pos.trDelta ); VectorCopy( p, le->pos.trBase ); VectorCopy( p, re->origin ); re->customShader = hShader; // rage pro can't alpha fade, so use a different shader if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { re->customShader = cgs.media.smokePuffRageProShader; re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; re->shaderRGBA[3] = 0xff; } else { re->shaderRGBA[0] = le->color[0] * 0xff; re->shaderRGBA[1] = le->color[1] * 0xff; re->shaderRGBA[2] = le->color[2] * 0xff; re->shaderRGBA[3] = 0xff; } re->reType = RT_SPRITE; re->radius = le->radius; return le; } /* ================== CG_SpawnEffect Player teleporting in or out ================== */ void CG_SpawnEffect( vec3_t org ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_FADE_RGB; le->startTime = cg.time; le->endTime = cg.time + 500; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; re = &le->refEntity; re->reType = RT_MODEL; re->shaderTime = cg.time / 1000.0f; #ifndef MISSIONPACK re->customShader = cgs.media.teleportEffectShader; #endif re->hModel = cgs.media.teleportEffectModel; AxisClear( re->axis ); VectorCopy( org, re->origin ); #ifdef MISSIONPACK re->origin[2] += 16; #else re->origin[2] -= 24; #endif } /* =============== CG_LightningBoltBeam =============== */ void CG_LightningBoltBeam( vec3_t start, vec3_t end ) { localEntity_t *le; refEntity_t *beam; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_SHOWREFENTITY; le->startTime = cg.time; le->endTime = cg.time + 50; beam = &le->refEntity; VectorCopy( start, beam->origin ); // this is the end point VectorCopy( end, beam->oldorigin ); beam->reType = RT_LIGHTNING; beam->customShader = cgs.media.lightningShader; } /* ================== CG_KamikazeEffect ================== */ void CG_KamikazeEffect( vec3_t org ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_KAMIKAZE; le->startTime = cg.time; le->endTime = cg.time + 3000;//2250; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; VectorClear(le->angles.trBase); re = &le->refEntity; re->reType = RT_MODEL; re->shaderTime = cg.time / 1000.0f; re->hModel = cgs.media.kamikazeEffectModel; VectorCopy( org, re->origin ); } /* ================== CG_ObeliskExplode ================== */ void CG_ObeliskExplode( vec3_t org, int entityNum ) { localEntity_t *le; vec3_t origin; // create an explosion VectorCopy( org, origin ); origin[2] += 64; le = CG_MakeExplosion( origin, vec3_origin, cgs.media.dishFlashModel, cgs.media.rocketExplosionShader, 600, qtrue ); le->light = 300; le->lightColor[0] = 1; le->lightColor[1] = 0.75; le->lightColor[2] = 0.0; } /* ================== CG_ObeliskPain ================== */ void CG_ObeliskPain( vec3_t org ) { float r; sfxHandle_t sfx; // hit sound r = rand() & 3; if ( r < 2 ) { sfx = cgs.media.obeliskHitSound1; } else if ( r == 2 ) { sfx = cgs.media.obeliskHitSound2; } else { sfx = cgs.media.obeliskHitSound3; } trap_S_StartSound ( org, ENTITYNUM_NONE, CHAN_BODY, sfx ); } /* ================== CG_InvulnerabilityImpact ================== */ void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ) { localEntity_t *le; refEntity_t *re; int r; sfxHandle_t sfx; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_INVULIMPACT; le->startTime = cg.time; le->endTime = cg.time + 1000; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; re = &le->refEntity; re->reType = RT_MODEL; re->shaderTime = cg.time / 1000.0f; re->hModel = cgs.media.invulnerabilityImpactModel; VectorCopy( org, re->origin ); AnglesToAxis( angles, re->axis ); r = rand() & 3; if ( r < 2 ) { sfx = cgs.media.invulnerabilityImpactSound1; } else if ( r == 2 ) { sfx = cgs.media.invulnerabilityImpactSound2; } else { sfx = cgs.media.invulnerabilityImpactSound3; } trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, sfx ); } /* ================== CG_InvulnerabilityJuiced ================== */ void CG_InvulnerabilityJuiced( vec3_t org ) { localEntity_t *le; refEntity_t *re; vec3_t angles; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_INVULJUICED; le->startTime = cg.time; le->endTime = cg.time + 10000; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; re = &le->refEntity; re->reType = RT_MODEL; re->shaderTime = cg.time / 1000.0f; re->hModel = cgs.media.invulnerabilityJuicedModel; VectorCopy( org, re->origin ); VectorClear(angles); AnglesToAxis( angles, re->axis ); trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, cgs.media.invulnerabilityJuicedSound ); } /* ================== CG_ScorePlum ================== */ void CG_ScorePlum( int client, vec3_t org, int score ) { localEntity_t *le; refEntity_t *re; vec3_t angles; static vec3_t lastPos; // only visualize for the client that scored if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) { return; } le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_SCOREPLUM; le->startTime = cg.time; le->endTime = cg.time + 4000; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; le->radius = score; VectorCopy( org, le->pos.trBase ); if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) { le->pos.trBase[2] -= 20; } //CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos)); VectorCopy(org, lastPos); re = &le->refEntity; re->reType = RT_SPRITE; re->radius = 16; VectorClear(angles); AnglesToAxis( angles, re->axis ); } /* ==================== CG_MakeExplosion ==================== */ localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, qhandle_t hModel, qhandle_t shader, int msec, qboolean isSprite ) { float ang; localEntity_t *ex; int offset; vec3_t tmpVec, newOrigin; if ( msec <= 0 ) { CG_Error( "CG_MakeExplosion: msec = %i", msec ); } // skew the time a bit so they aren't all in sync offset = rand() & 63; ex = CG_AllocLocalEntity(); if ( isSprite ) { ex->leType = LE_SPRITE_EXPLOSION; // randomly rotate sprite orientation ex->refEntity.rotation = rand() % 360; VectorScale( dir, 16, tmpVec ); VectorAdd( tmpVec, origin, newOrigin ); } else { ex->leType = LE_EXPLOSION; VectorCopy( origin, newOrigin ); // set axis with random rotate if ( !dir ) { AxisClear( ex->refEntity.axis ); } else { ang = rand() % 360; VectorCopy( dir, ex->refEntity.axis[0] ); RotateAroundDirection( ex->refEntity.axis, ang ); } } ex->startTime = cg.time - offset; ex->endTime = ex->startTime + msec; // bias the time so all shader effects start correctly ex->refEntity.shaderTime = ex->startTime / 1000.0f; ex->refEntity.hModel = hModel; ex->refEntity.customShader = shader; // set origin VectorCopy( newOrigin, ex->refEntity.origin ); VectorCopy( newOrigin, ex->refEntity.oldorigin ); ex->color[0] = ex->color[1] = ex->color[2] = 1.0; return ex; } /* ================= CG_Bleed This is the spurt of blood when a character gets hit ================= */ void CG_Bleed( vec3_t origin, int entityNum ) { localEntity_t *ex; if ( !cg_blood.integer ) { return; } ex = CG_AllocLocalEntity(); ex->leType = LE_EXPLOSION; ex->startTime = cg.time; ex->endTime = ex->startTime + 500; VectorCopy ( origin, ex->refEntity.origin); ex->refEntity.reType = RT_SPRITE; ex->refEntity.rotation = rand() % 360; ex->refEntity.radius = 24; ex->refEntity.customShader = cgs.media.bloodExplosionShader; // don't show player's own blood in view if ( entityNum == cg.snap->ps.clientNum ) { ex->refEntity.renderfx |= RF_THIRD_PERSON; } } /* ================== CG_SpurtBlood (LEILEI) ================== */ void CG_SpurtBlood( vec3_t origin, vec3_t velocity, int hard ) { localEntity_t *blood; // if ( !cg_blood.integer ) { return; } velocity[0] = velocity[0] * hard * crandom()*460; velocity[1] = velocity[1] * hard * crandom()*460; velocity[2] = velocity[2] * hard * crandom()*566 + 65; blood = CG_SmokePuff( origin, velocity, 21, // radius 1, 1, 1, 1, // color 2450, // trailTime cg.time, // startTime 0, // fadeInTime 0, // flags cgs.media.lbldShader1 ); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; blood->leType = LE_GORE; blood->pos.trType = TR_GRAVITY; VectorCopy( velocity, blood->pos.trDelta ); blood->pos.trDelta[2] = 55; if (crandom() < 0.5){ blood->leMarkType = LEMT_BURN; blood->leBounceSoundType = LEBS_BLOOD; } // VectorCopy( velocity, blood->pos.trDelta ); } /* ================== CG_LaunchGib ================== */ void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + 5000 + random() * 3000; VectorCopy( origin, re->origin ); AxisCopy( axisDefault, re->axis ); re->hModel = hModel; le->pos.trType = TR_GRAVITY; VectorCopy( origin, le->pos.trBase ); VectorCopy( velocity, le->pos.trDelta ); le->pos.trTime = cg.time; le->bounceFactor = 0.6f; le->leBounceSoundType = LEBS_BLOOD; le->leMarkType = LEMT_BLOOD; if ( cg_leiSuperGoreyAwesome.integer ) { CG_SpurtBlood( origin, velocity, 7); // LEILEI toss some extra juice CG_SpurtBlood( origin, velocity, 22); CG_SpurtBlood( origin, velocity, 11); } } /* =================== CG_GibPlayer Generated a bunch of gibs launching out from the bodies location =================== */ #define GIB_VELOCITY 250 #define GIB_JUMP 250 void CG_GibPlayer( vec3_t playerOrigin ) { vec3_t origin, velocity; if ( !cg_blood.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; if ( rand() & 1 ) { CG_LaunchGib( origin, velocity, cgs.media.gibSkull ); } else { CG_LaunchGib( origin, velocity, cgs.media.gibBrain ); } // allow gibs to be turned off for speed if ( !cg_gibs.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibArm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibChest ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFist ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFoot ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibForearm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibIntestine ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); } /* ================== CG_LaunchGib ================== */ void CG_LaunchExplode( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + 10000 + random() * 6000; VectorCopy( origin, re->origin ); AxisCopy( axisDefault, re->axis ); re->hModel = hModel; le->pos.trType = TR_GRAVITY; VectorCopy( origin, le->pos.trBase ); VectorCopy( velocity, le->pos.trDelta ); le->pos.trTime = cg.time; le->bounceFactor = 0.1f; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; } #define EXP_VELOCITY 100 #define EXP_JUMP 150 /* =================== CG_GibPlayer Generated a bunch of gibs launching out from the bodies location =================== */ void CG_BigExplosion( vec3_t playerOrigin ) { vec3_t origin, velocity; if ( !cg_blood.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY; velocity[1] = crandom()*EXP_VELOCITY; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY; velocity[1] = crandom()*EXP_VELOCITY; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*1.5; velocity[1] = crandom()*EXP_VELOCITY*1.5; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*2.0; velocity[1] = crandom()*EXP_VELOCITY*2.0; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*2.5; velocity[1] = crandom()*EXP_VELOCITY*2.5; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); } openarena_0.8.8.orig/code/cgame/cg_drawtools.c0000644000175000017500000004221411656310265020053 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc #include "cg_local.h" /* ================ CG_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { #if 0 // adjust for wide screens if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); } #endif // scale for screen sizes *x *= cgs.screenXScale; *y *= cgs.screenYScale; *w *= cgs.screenXScale; *h *= cgs.screenYScale; } /* ================ CG_FillRect Coordinates are 640*480 virtual values ================= */ void CG_FillRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } /* ================ CG_DrawSides Coords are virtual 640x480 ================ */ void CG_DrawSides(float x, float y, float w, float h, float size) { CG_AdjustFrom640( &x, &y, &w, &h ); size *= cgs.screenXScale; trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); } void CG_DrawTopBottom(float x, float y, float w, float h, float size) { CG_AdjustFrom640( &x, &y, &w, &h ); size *= cgs.screenYScale; trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); } /* ================ UI_DrawRect Coordinates are 640*480 virtual values ================= */ void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) { trap_R_SetColor( color ); CG_DrawTopBottom(x, y, width, height, size); CG_DrawSides(x, y, width, height, size); trap_R_SetColor( NULL ); } /* ================ CG_DrawPic Coordinates are 640*480 virtual values ================= */ void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* =============== CG_DrawChar Coordinates and size in 640*480 virtual screen size =============== */ void CG_DrawChar( int x, int y, int width, int height, int ch ) { int row, col; float frow, fcol; float size; float ax, ay, aw, ah; ch &= 255; if ( ch == ' ' ) { return; } ax = x; ay = y; aw = width; ah = height; CG_AdjustFrom640( &ax, &ay, &aw, &ah ); row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size, cgs.media.charsetShader ); } /* ================== CG_DrawStringExt Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. Coordinates are at 640 by 480 virtual resolution ================== */ void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { vec4_t color; const char *s; int xx; int cnt; if (maxChars <= 0) maxChars = 32767; // do them all! // draw the drop shadow if (shadow) { color[0] = color[1] = color[2] = 0; color[3] = setColor[3]; trap_R_SetColor( color ); s = string; xx = x; cnt = 0; while ( *s && cnt < maxChars) { if ( Q_IsColorString( s ) ) { s += 2; continue; } CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s ); cnt++; xx += charWidth; s++; } } // draw the colored text s = string; xx = x; cnt = 0; trap_R_SetColor( setColor ); while ( *s && cnt < maxChars) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; trap_R_SetColor( color ); } s += 2; continue; } CG_DrawChar( xx, y, charWidth, charHeight, *s ); xx += charWidth; cnt++; s++; } trap_R_SetColor( NULL ); } void CG_DrawBigString( int x, int y, const char *s, float alpha ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; CG_DrawStringExt( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); } void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { CG_DrawStringExt( x, y, s, color, qtrue, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); } void CG_DrawSmallString( int x, int y, const char *s, float alpha ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; CG_DrawStringExt( x, y, s, color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) { CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } /* ================= CG_DrawStrlen Returns character count, skiping color escape codes ================= */ int CG_DrawStrlen( const char *str ) { const char *s = str; int count = 0; while ( *s ) { if ( Q_IsColorString( s ) ) { s += 2; } else { count++; s++; } } return count; } /* ============= CG_TileClearBox This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) { float s1, t1, s2, t2; s1 = x/64.0; t1 = y/64.0; s2 = (x+w)/64.0; t2 = (y+h)/64.0; trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader ); } /* ============== CG_TileClear Clear around a sized down screen ============== */ void CG_TileClear( void ) { int top, bottom, left, right; int w, h; w = cgs.glconfig.vidWidth; h = cgs.glconfig.vidHeight; if ( cg.refdef.x == 0 && cg.refdef.y == 0 && cg.refdef.width == w && cg.refdef.height == h ) { return; // full screen rendering } top = cg.refdef.y; bottom = top + cg.refdef.height-1; left = cg.refdef.x; right = left + cg.refdef.width-1; // clear above view screen CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader ); // clear below view screen CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader ); // clear left of view screen CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader ); // clear right of view screen CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader ); } /* ================ CG_FadeColor ================ */ float *CG_FadeColor( int startMsec, int totalMsec ) { static vec4_t color; int t; if ( startMsec == 0 ) { return NULL; } t = cg.time - startMsec; if ( t >= totalMsec ) { return NULL; } // fade out if ( totalMsec - t < FADE_TIME ) { color[3] = ( totalMsec - t ) * 1.0/FADE_TIME; } else { color[3] = 1.0; } color[0] = color[1] = color[2] = 1; return color; } /* ================ CG_TeamColor ================ */ float *CG_TeamColor( int team ) { static vec4_t red = {1, 0.2f, 0.2f, 1}; static vec4_t blue = {0.2f, 0.2f, 1, 1}; static vec4_t other = {1, 1, 1, 1}; static vec4_t spectator = {0.7f, 0.7f, 0.7f, 1}; switch ( team ) { case TEAM_RED: return red; case TEAM_BLUE: return blue; case TEAM_SPECTATOR: return spectator; default: return other; } } /* ================= CG_GetColorForHealth ================= */ void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ) { int count; int max; // calculate the total points of damage that can // be sustained at the current health / armor level if ( health <= 0 ) { VectorClear( hcolor ); // black hcolor[3] = 1; return; } count = armor; max = health * ARMOR_PROTECTION / ( 1.0 - ARMOR_PROTECTION ); if ( max < count ) { count = max; } health += count; // set the color based on health hcolor[0] = 1.0; hcolor[3] = 1.0; if ( health >= 100 ) { hcolor[2] = 1.0; } else if ( health < 66 ) { hcolor[2] = 0; } else { hcolor[2] = ( health - 66 ) / 33.0; } if ( health > 60 ) { hcolor[1] = 1.0; } else if ( health < 30 ) { hcolor[1] = 0; } else { hcolor[1] = ( health - 30 ) / 30.0; } } /* ================= CG_ColorForHealth ================= */ void CG_ColorForHealth( vec4_t hcolor ) { CG_GetColorForHealth( cg.snap->ps.stats[STAT_HEALTH], cg.snap->ps.stats[STAT_ARMOR], hcolor ); } // bk001205 - code below duplicated in q3_ui/ui-atoms.c // bk001205 - FIXME: does this belong in ui_shared.c? /* ================= UI_DrawProportionalString2 ================= */ static int propMap[128][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, PROP_SPACE_WIDTH}, // SPACE {11, 122, 7}, // ! {154, 181, 14}, // " {55, 122, 17}, // # {79, 122, 18}, // $ {101, 122, 23}, // % {153, 122, 18}, // & {9, 93, 7}, // ' {207, 122, 8}, // ( {230, 122, 9}, // ) {177, 122, 18}, // * {30, 152, 18}, // + {85, 181, 7}, // , {34, 93, 11}, // - {110, 181, 6}, // . {130, 152, 14}, // / {22, 64, 17}, // 0 {41, 64, 12}, // 1 {58, 64, 17}, // 2 {78, 64, 18}, // 3 {98, 64, 19}, // 4 {120, 64, 18}, // 5 {141, 64, 18}, // 6 {204, 64, 16}, // 7 {162, 64, 17}, // 8 {182, 64, 18}, // 9 {59, 181, 7}, // : {35,181, 7}, // ; {203, 152, 14}, // < {56, 93, 14}, // = {228, 152, 14}, // > {177, 181, 18}, // ? {28, 122, 22}, // @ {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {60, 152, 7}, // [ {106, 151, 13}, // '\' {83, 152, 7}, // ] {128, 122, 17}, // ^ {4, 152, 21}, // _ {134, 181, 5}, // ' {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {153, 152, 13}, // { {11, 181, 5}, // | {180, 152, 13}, // } {79, 93, 17}, // ~ {0, 0, -1} // DEL }; static int propMapB[26][3] = { {11, 12, 33}, {49, 12, 31}, {85, 12, 31}, {120, 12, 30}, {156, 12, 21}, {183, 12, 21}, {207, 12, 32}, {13, 55, 30}, {49, 55, 13}, {66, 55, 29}, {101, 55, 31}, {135, 55, 21}, {158, 55, 40}, {204, 55, 32}, {12, 97, 31}, {48, 97, 31}, {82, 97, 30}, {118, 97, 30}, {153, 97, 30}, {185, 97, 25}, {213, 97, 30}, {11, 139, 32}, {42, 139, 51}, {93, 139, 32}, {126, 139, 31}, {158, 139, 25}, }; #define PROPB_GAP_WIDTH 4 #define PROPB_SPACE_WIDTH 12 #define PROPB_HEIGHT 36 /* ================= UI_DrawBannerString ================= */ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color ) { const char* s; unsigned char ch; // bk001204 : array subscript float ax; float ay; float aw; float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * cgs.screenXScale + cgs.screenXBias; ay = y * cgs.screenYScale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* cgs.screenXScale; } else if ( ch >= 'A' && ch <= 'Z' ) { ch -= 'A'; fcol = (float)propMapB[ch][0] / 256.0f; frow = (float)propMapB[ch][1] / 256.0f; fwidth = (float)propMapB[ch][2] / 256.0f; fheight = (float)PROPB_HEIGHT / 256.0f; aw = (float)propMapB[ch][2] * cgs.screenXScale; ah = (float)PROPB_HEIGHT * cgs.screenXScale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, cgs.media.charsetPropB ); ax += (aw + (float)PROPB_GAP_WIDTH * cgs.screenXScale); } s++; } trap_R_SetColor( NULL ); } void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) { const char * s; int ch; int width; vec4_t drawcolor; // find the width of the drawn text s = str; width = 0; while ( *s ) { ch = *s; if ( ch == ' ' ) { width += PROPB_SPACE_WIDTH; } else if ( ch >= 'A' && ch <= 'Z' ) { width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH; } s++; } width -= PROPB_GAP_WIDTH; switch( style & UI_FORMATMASK ) { case UI_CENTER: x -= width / 2; break; case UI_RIGHT: x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawBannerString2( x+2, y+2, str, drawcolor ); } UI_DrawBannerString2( x, y, str, color ); } int UI_ProportionalStringWidth( const char* str ) { const char * s; int ch; int charWidth; int width; s = str; width = 0; while ( *s ) { ch = *s & 127; charWidth = propMap[ch][2]; if ( charWidth != -1 ) { width += charWidth; width += PROP_GAP_WIDTH; } s++; } width -= PROP_GAP_WIDTH; return width; } static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset ) { const char* s; unsigned char ch; // bk001204 - unsigned float ax; float ay; float aw; float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * cgs.screenXScale + cgs.screenXBias; ay = y * cgs.screenXScale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale; } else if ( propMap[ch][2] != -1 ) { fcol = (float)propMap[ch][0] / 256.0f; frow = (float)propMap[ch][1] / 256.0f; fwidth = (float)propMap[ch][2] / 256.0f; fheight = (float)PROP_HEIGHT / 256.0f; aw = (float)propMap[ch][2] * cgs.screenXScale * sizeScale; ah = (float)PROP_HEIGHT * cgs.screenXScale * sizeScale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset ); } else { aw = 0; } ax += (aw + (float)PROP_GAP_WIDTH * cgs.screenXScale * sizeScale); s++; } trap_R_SetColor( NULL ); } /* ================= UI_ProportionalSizeScale ================= */ float UI_ProportionalSizeScale( int style ) { if( style & UI_SMALLFONT ) { return 0.75; } return 1.00; } /* ================= UI_DrawProportionalString ================= */ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) { vec4_t drawcolor; int width; float sizeScale; sizeScale = UI_ProportionalSizeScale( style ); switch( style & UI_FORMATMASK ) { case UI_CENTER: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width / 2; break; case UI_RIGHT: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, cgs.media.charsetProp ); } if ( style & UI_INVERSE ) { drawcolor[0] = color[0] * 0.8; drawcolor[1] = color[1] * 0.8; drawcolor[2] = color[2] * 0.8; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetProp ); return; } if ( style & UI_PULSE ) { drawcolor[0] = color[0] * 0.8; drawcolor[1] = color[1] * 0.8; drawcolor[2] = color[2] * 0.8; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); drawcolor[0] = color[0]; drawcolor[1] = color[1]; drawcolor[2] = color[2]; drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR ); UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow ); return; } UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); } openarena_0.8.8.orig/code/cgame/cg_draw.c0000644000175000017500000023437311656310265017003 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay #include "cg_local.h" #ifdef MISSIONPACK #include "../ui/ui_shared.h" // used for scoreboard extern displayContextDef_t cgDC; menuDef_t *menuScoreboard = NULL; #else int drawTeamOverlayModificationCount = -1; #endif int sortedTeamPlayers[TEAM_MAXOVERLAY]; int numSortedTeamPlayers; char systemChat[256]; char teamChat1[256]; char teamChat2[256]; #ifdef MISSIONPACK int CG_Text_Width(const char *text, float scale, int limit) { int count,len; float out; glyphInfo_t *glyph; float useScale; // FIXME: see ui_main.c, same problem // const unsigned char *s = text; const char *s = text; fontInfo_t *font = &cgDC.Assets.textFont; if (scale <= cg_smallFont.value) { font = &cgDC.Assets.smallFont; } else if (scale > cg_bigFont.value) { font = &cgDC.Assets.bigFont; } useScale = scale * font->glyphScale; out = 0; if (text) { len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { if ( Q_IsColorString(s) ) { s += 2; continue; } else { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build out += glyph->xSkip; s++; count++; } } } return out * useScale; } int CG_Text_Height(const char *text, float scale, int limit) { int len, count; float max; glyphInfo_t *glyph; float useScale; // TTimo: FIXME // const unsigned char *s = text; const char *s = text; fontInfo_t *font = &cgDC.Assets.textFont; if (scale <= cg_smallFont.value) { font = &cgDC.Assets.smallFont; } else if (scale > cg_bigFont.value) { font = &cgDC.Assets.bigFont; } useScale = scale * font->glyphScale; max = 0; if (text) { len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { if ( Q_IsColorString(s) ) { s += 2; continue; } else { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build if (max < glyph->height) { max = glyph->height; } s++; count++; } } } return max * useScale; } void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) { float w, h; w = width * scale; h = height * scale; CG_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); } void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) { int len, count; vec4_t newColor; glyphInfo_t *glyph; float useScale; fontInfo_t *font = &cgDC.Assets.textFont; if (scale <= cg_smallFont.value) { font = &cgDC.Assets.smallFont; } else if (scale > cg_bigFont.value) { font = &cgDC.Assets.bigFont; } useScale = scale * font->glyphScale; if (text) { // TTimo: FIXME // const unsigned char *s = text; const char *s = text; trap_R_SetColor( color ); memcpy(&newColor[0], &color[0], sizeof(vec4_t)); len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; colorBlack[3] = newColor[3]; trap_R_SetColor( colorBlack ); CG_Text_PaintChar(x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); colorBlack[3] = 1.0; trap_R_SetColor( newColor ); } CG_Text_PaintChar(x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); // CG_DrawPic(x, y - yadj, scale * cgDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * cgDC.Assets.textFont.glyphs[text[i]].imageHeight, cgDC.Assets.textFont.glyphs[text[i]].glyph); x += (glyph->xSkip * useScale) + adjust; s++; count++; } } trap_R_SetColor( NULL ); } } #endif /* ============== CG_DrawField Draws large numbers for status bar and powerups ============== */ #ifndef MISSIONPACK static void CG_DrawField (int x, int y, int width, int value) { char num[16], *ptr; int l; int frame; if ( width < 1 ) { return; } // draw number string if ( width > 5 ) { width = 5; } switch ( width ) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf (num, sizeof(num), "%i", value); l = strlen(num); if (l > width) l = width; x += 2 + CHAR_WIDTH*(width - l); ptr = num; while (*ptr && l) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] ); x += CHAR_WIDTH; ptr++; l--; } } #endif // MISSIONPACK /* ================ CG_Draw3DModel ================ */ void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) { refdef_t refdef; refEntity_t ent; if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) { return; } CG_AdjustFrom640( &x, &y, &w, &h ); memset( &refdef, 0, sizeof( refdef ) ); memset( &ent, 0, sizeof( ent ) ); AnglesToAxis( angles, ent.axis ); VectorCopy( origin, ent.origin ); ent.hModel = model; ent.customSkin = skin; ent.renderfx = RF_NOSHADOW; // no stencil shadows refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.fov_x = 30; refdef.fov_y = 30; refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.time = cg.time; trap_R_ClearScene(); trap_R_AddRefEntityToScene( &ent ); trap_R_RenderScene( &refdef ); } /* ================ CG_DrawHead Used for both the status bar and the scoreboard ================ */ void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) { clipHandle_t cm; clientInfo_t *ci; float len; vec3_t origin; vec3_t mins, maxs; ci = &cgs.clientinfo[ clientNum ]; if ( cg_draw3dIcons.integer ) { cm = ci->headModel; if ( !cm ) { return; } // offset the origin y and z to center the head trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the head nearly fills the box // assume heads are taller than wide len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) // allow per-model tweaking VectorAdd( origin, ci->headOffset, origin ); CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles ); } else if ( cg_drawIcons.integer ) { CG_DrawPic( x, y, w, h, ci->modelIcon ); } // if they are deferred, draw a cross out if ( ci->deferred ) { CG_DrawPic( x, y, w, h, cgs.media.deferShader ); } } /* ================ CG_DrawFlagModel Used for both the status bar and the scoreboard ================ */ void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) { qhandle_t cm; float len; vec3_t origin, angles; vec3_t mins, maxs; qhandle_t handle; if ( !force2D && cg_draw3dIcons.integer ) { VectorClear( angles ); cm = cgs.media.redFlagModel; // offset the origin y and z to center the flag trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the flag nearly fills the box // assume heads are taller than wide len = 0.5 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) angles[YAW] = 60 * sin( cg.time / 2000.0 );; if( team == TEAM_RED ) { handle = cgs.media.redFlagModel; if(cgs.gametype == GT_DOUBLE_D){ if(cgs.redflag == TEAM_BLUE) handle = cgs.media.blueFlagModel; if(cgs.redflag == TEAM_FREE) handle = cgs.media.neutralFlagModel; if(cgs.redflag == TEAM_NONE) handle = cgs.media.neutralFlagModel; } } else if( team == TEAM_BLUE ) { handle = cgs.media.blueFlagModel; if(cgs.gametype == GT_DOUBLE_D){ if(cgs.redflag == TEAM_BLUE) handle = cgs.media.blueFlagModel; if(cgs.redflag == TEAM_FREE) handle = cgs.media.neutralFlagModel; if(cgs.redflag == TEAM_NONE) handle = cgs.media.neutralFlagModel; } } else if( team == TEAM_FREE ) { handle = cgs.media.neutralFlagModel; } else { return; } CG_Draw3DModel( x, y, w, h, handle, 0, origin, angles ); } else if ( cg_drawIcons.integer ) { gitem_t *item; if( team == TEAM_RED ) { item = BG_FindItemForPowerup( PW_REDFLAG ); } else if( team == TEAM_BLUE ) { item = BG_FindItemForPowerup( PW_BLUEFLAG ); } else if( team == TEAM_FREE ) { item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); } else { return; } if (item) { CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon ); } } } /* ================ CG_DrawStatusBarHead ================ */ #ifndef MISSIONPACK static void CG_DrawStatusBarHead( float x ) { vec3_t angles; float size, stretch; float frac; VectorClear( angles ); if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) { frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME; size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 ); stretch = size - ICON_SIZE * 1.25; // kick in the direction of damage x -= stretch * 0.5 + cg.damageX * stretch * 0.5; cg.headStartYaw = 180 + cg.damageX * 45; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); cg.headStartTime = cg.time; cg.headEndTime = cg.time + 100 + random() * 2000; } else { if ( cg.time >= cg.headEndTime ) { // select a new head angle cg.headStartYaw = cg.headEndYaw; cg.headStartPitch = cg.headEndPitch; cg.headStartTime = cg.headEndTime; cg.headEndTime = cg.time + 100 + random() * 2000; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); } size = ICON_SIZE * 1.25; } // if the server was frozen for a while we may have a bad head start time if ( cg.headStartTime > cg.time ) { cg.headStartTime = cg.time; } frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime ); frac = frac * frac * ( 3 - 2 * frac ); angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac; angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac; CG_DrawHead( x, 480 - size, size, size, cg.snap->ps.clientNum, angles ); } #endif // MISSIONPACK /* ================ CG_DrawStatusBarFlag ================ */ #ifndef MISSIONPACK static void CG_DrawStatusBarFlag( float x, int team ) { CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse ); } #endif // MISSIONPACK /* ================ CG_DrawTeamBackground ================ */ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) { vec4_t hcolor; hcolor[3] = alpha; if ( team == TEAM_RED ) { hcolor[0] = 1; hcolor[1] = 0; hcolor[2] = 0; } else if ( team == TEAM_BLUE ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 1; } else { return; } trap_R_SetColor( hcolor ); CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); } /* ================ CG_DrawStatusBar ================ */ #ifndef MISSIONPACK static void CG_DrawStatusBar( void ) { int color; centity_t *cent; playerState_t *ps; int value; vec4_t hcolor; vec3_t angles; vec3_t origin; qhandle_t handle; static float colors[4][4] = { // { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; { 1.0f, 0.69f, 0.0f, 1.0f }, // normal { 1.0f, 0.2f, 0.2f, 1.0f }, // low health { 0.5f, 0.5f, 0.5f, 1.0f }, // weapon firing { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 if ( cg_drawStatus.integer == 0 ) { return; } // draw the team background if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) //If not following anybody: CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] ); else //Sago: If we follow find the teamcolor of the guy we follow. It might not be our own team! CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cgs.clientinfo[ cg.snap->ps.clientNum ].team ); cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; VectorClear( angles ); // draw any 3D icons first, so the changes back to 2D are minimized if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { origin[0] = 70; origin[1] = 0; origin[2] = 0; angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); } CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE ); if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED ); } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE ); } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE ); } if ( ps->stats[ STAT_ARMOR ] ) { origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorModel, 0, origin, angles ); } if( cgs.gametype == GT_HARVESTER ) { origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { handle = cgs.media.redCubeModel; } else { handle = cgs.media.blueCubeModel; } CG_Draw3DModel( 470 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, handle, 0, origin, angles ); } // // ammo // if ( cent->currentState.weapon ) { value = ps->ammo[cent->currentState.weapon]; if ( value > -1 ) { if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING && cg.predictedPlayerState.weaponTime > 100 ) { // draw as dark grey when reloading color = 2; // dark grey } else { if ( value >= 0 ) { color = 0; // green } else { color = 1; // red } } trap_R_SetColor( colors[color] ); CG_DrawField (0, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for ammo if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { qhandle_t icon; icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; if ( icon ) { CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon ); } } } } // // health // value = ps->stats[STAT_HEALTH]; if ( value > 100 ) { trap_R_SetColor( colors[3] ); // white } else if (value > 25) { trap_R_SetColor( colors[0] ); // green } else if (value > 0) { color = (cg.time >> 8) & 1; // flash trap_R_SetColor( colors[color] ); } else { trap_R_SetColor( colors[1] ); // red } // stretch the health up when taking damage CG_DrawField ( 185, 432, 3, value); CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); // // armor // value = ps->stats[STAT_ARMOR]; if (value > 0 ) { trap_R_SetColor( colors[0] ); CG_DrawField (370, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for armor if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon ); } } //Skulls! if(cgs.gametype == GT_HARVESTER) { value = ps->generic1; if (value > 0 ) { trap_R_SetColor( colors[0] ); CG_DrawField (470, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for skull if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { handle = cgs.media.redCubeIcon; } else { handle = cgs.media.blueCubeIcon; } CG_DrawPic( 470 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, handle ); } } } } #endif /* =========================================================================================== UPPER RIGHT CORNER =========================================================================================== */ /* ================ CG_DrawAttacker ================ */ static float CG_DrawAttacker( float y ) { int t; float size; vec3_t angles; const char *info; const char *name; int clientNum; if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return y; } if ( !cg.attackerTime ) { return y; } clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) { return y; } t = cg.time - cg.attackerTime; if ( t > ATTACKER_HEAD_TIME ) { cg.attackerTime = 0; return y; } size = ICON_SIZE * 1.25; angles[PITCH] = 0; angles[YAW] = 180; angles[ROLL] = 0; CG_DrawHead( 640 - size, y, size, size, clientNum, angles ); info = CG_ConfigString( CS_PLAYERS + clientNum ); name = Info_ValueForKey( info, "n" ); y += size; CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 ); return y + BIGCHAR_HEIGHT + 2; } /* ================ CG_DrawSpeedMeter ================ */ static float CG_DrawSpeedMeter( float y ) { char *s; int w; vec_t *vel; int speed; /* speed meter can get in the way of the scoreboard */ if ( cg.scoreBoardShowing ) { return y; } vel = cg.snap->ps.velocity; /* ignore vertical component of velocity */ speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); s = va( "%iu/s", speed ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; if (cg_drawSpeed.integer == 1) { /* top left-hand corner of screen */ CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } else { /* center of screen */ CG_DrawBigString( 320 - w / 2, 300, s, 1.0F); return y; } } /* ================== CG_DrawSnapshot ================== */ static float CG_DrawSnapshot( float y ) { char *s; int w; s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ================== CG_DrawFPS ================== */ #define FPS_FRAMES 4 static float CG_DrawFPS( float y ) { char *s; int w; static int previousTimes[FPS_FRAMES]; static int index; int i, total; int fps; static int previous; int t, frameTime; // don't use serverTime, because that will be drifting to // correct for internet lag changes, timescales, timedemos, etc t = trap_Milliseconds(); frameTime = t - previous; previous = t; previousTimes[index % FPS_FRAMES] = frameTime; index++; if ( index > FPS_FRAMES ) { // average multiple frames together to smooth changes out a bit total = 0; for ( i = 0 ; i < FPS_FRAMES ; i++ ) { total += previousTimes[i]; } if ( !total ) { total = 1; } fps = 1000 * FPS_FRAMES / total; s = va( "%ifps", fps ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); } return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTimer ================= */ static float CG_DrawTimer( float y ) { char *s; int w; int mins, seconds, tens; int msec; msec = cg.time - cgs.levelStartTime; seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; s = va( "%i:%i%i", mins, tens, seconds ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* CG_DrawDoubleDominationThings * *Sago: Might be relevant for debugging missionpack. */ /*static float CG_DrawDoubleDominationThings( float y ) { char *s; int w; int statusA, statusB; statusA = cgs.redflag; statusB = cgs.blueflag; if(statusA == TEAM_NONE) { s = va("Point A not spawned"); } else if(statusA == TEAM_FREE) { s = va("Point A is not controlled"); } else if(statusA == TEAM_RED) { s = va("Point A is controlled by RED"); } else if(statusA == TEAM_BLUE) { s = va("Point A is controlled by BLUE"); } else s = va("Point A has an error"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); y+=SMALLCHAR_HEIGHT+4; if(statusB == TEAM_NONE) { s = va("Point B not spawned"); } else if(statusB == TEAM_FREE) { s = va("Point B is not controlled"); } else if(statusB == TEAM_RED) { s = va("Point B is controlled by RED"); } else if(statusB == TEAM_BLUE) { s = va("Point B is controlled by BLUE"); } else s = va("Point B has an error"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); if( ( ( statusB == statusA ) && ( statusA == TEAM_RED ) ) || ( ( statusB == statusA ) && ( statusA == TEAM_BLUE ) ) ) { s = va("Capture in: %i",(cgs.timetaken+10*1000-cg.time)/1000+1); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; y+=SMALLCHAR_HEIGHT+4; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); } return y + SMALLCHAR_HEIGHT+4; }*/ /* ================= CG_DrawLMSmode ================= */ static float CG_DrawLMSmode( float y ) { char *s; int w; if(cgs.lms_mode == 0) { s = va("LMS: Point/round + OT"); } else if(cgs.lms_mode == 1) { s = va("LMS: Point/round - OT"); } else if(cgs.lms_mode == 2) { s = va("LMS: Point/kill + OT"); } else if(cgs.lms_mode == 3) { s = va("LMS: Point/kill - OT"); } else s = va("LMS: Unknown mode"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT+4; } /* ================= CG_DrawCTFoneway ================= */ static float CG_DrawCTFoneway( float y ) { char *s; int w; vec4_t color; if(cgs.gametype != GT_CTF_ELIMINATION) return y; memcpy(color,g_color_table[ColorIndex(COLOR_WHITE)],sizeof(color)); if( (cgs.elimflags&EF_ONEWAY)==0) { return y; //nothing to draw } else if(cgs.attackingTeam == TEAM_BLUE) { memcpy(color,g_color_table[ColorIndex(COLOR_BLUE)],sizeof(color)); s = va("Blue team on offence"); } else if(cgs.attackingTeam == TEAM_RED) { memcpy(color,g_color_table[ColorIndex(COLOR_RED)],sizeof(color)); s = va("Red team on offence"); } else s = va("Unknown team on offence"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor( 635 - w, y + 2, s, color); return y + SMALLCHAR_HEIGHT+4; } /* ================= CG_DrawEliminationDeathMessage ================= */ /*static float CG_DrawEliminationDeathMessage( float y ) { char *s; int w; s = va("You are waiting for a new round"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT+4; }*/ /* ================= CG_DrawDomStatus ================= */ static float CG_DrawDomStatus( float y ) { int i,w; char *s; vec4_t color; for(i = 0;i < cgs.domination_points_count;i++) { switch(cgs.domination_points_status[i]) { case TEAM_RED: memcpy(color,g_color_table[ColorIndex(COLOR_RED)],sizeof(color)); break; case TEAM_BLUE: memcpy(color,g_color_table[ColorIndex(COLOR_BLUE)],sizeof(color)); break; default: memcpy(color,g_color_table[ColorIndex(COLOR_WHITE)],sizeof(color)); break; } s = va("%s",cgs.domination_points_names[i]); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor( 635 - w, y + 2, s, color); y += SMALLCHAR_HEIGHT+4; } return y; } /* ================= CG_DrawEliminationTimer ================= */ static float CG_DrawEliminationTimer( float y ) { char *s; int w; int mins, seconds, tens, sec; int msec; vec4_t color; const char *st; float scale; int cw; int rst; rst = cgs.roundStartTime; if(cg.time>rst && !cgs.roundtime) { return y; } //default color is white memcpy(color,g_color_table[ColorIndex(COLOR_WHITE)],sizeof(color)); //msec = cg.time - cgs.levelStartTime; if(cg.time>rst) //We are started { msec = cgs.roundtime*1000 - (cg.time -rst); if(msec<=30*1000-1) //<= 30 seconds memcpy(color,g_color_table[ColorIndex(COLOR_YELLOW)],sizeof(color)); if(msec<=10*1000-1) //<= 10 seconds memcpy(color,g_color_table[ColorIndex(COLOR_RED)],sizeof(color)); msec += 1000; //120-1 instead of 119-0 } else { //Warmup msec = -cg.time +rst; memcpy(color,g_color_table[ColorIndex(COLOR_GREEN)],sizeof(color)); sec = msec/1000; msec += 1000; //5-1 instead of 4-0 /*** Lots of stuff ****/ if(cg.warmup == 0) { st = va( "Round in: %i", sec + 1 ); if ( sec != cg.warmupCount ) { cg.warmupCount = sec; switch ( sec ) { case 0: trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); break; case 1: trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); break; case 2: trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); break; default: break; } } scale = 0.45f; switch ( cg.warmupCount ) { case 0: cw = 28; scale = 0.54f; break; case 1: cw = 24; scale = 0.51f; break; case 2: cw = 20; scale = 0.48f; break; default: cw = 16; scale = 0.45f; break; } #ifdef MISSIONPACK //w = CG_Text_Width(s, scale, 0); //CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, st, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); #else w = CG_DrawStrlen( st ); CG_DrawStringExt( 320 - w * cw/2, 70, st, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); #endif } /* Lots of stuff */ } seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; if(msec>=0) s = va( " %i:%i%i", mins, tens, seconds ); else s = va( " Overtime"); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigStringColor( 635 - w, y + 2, s, color); return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTeamOverlay ================= */ static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) { int x, w, h, xx; int i, j, len; const char *p; vec4_t hcolor; int pwidth, lwidth; int plyrs; char st[16]; clientInfo_t *ci; gitem_t *item; int ret_y, count; if ( !cg_drawTeamOverlay.integer ) { return y; } if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) { return y; // Not on any team } plyrs = 0; // max player name width pwidth = 0; count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { plyrs++; len = CG_DrawStrlen(ci->name); if (len > pwidth) pwidth = len; } } if (!plyrs) return y; if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH) pwidth = TEAM_OVERLAY_MAXNAME_WIDTH; // max location name width lwidth = 0; for (i = 1; i < MAX_LOCATIONS; i++) { p = CG_ConfigString(CS_LOCATIONS + i); if (p && *p) { len = CG_DrawStrlen(p); if (len > lwidth) lwidth = len; } } if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH) lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH; w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH; if ( right ) x = 640 - w; else x = 0; h = plyrs * TINYCHAR_HEIGHT; if ( upper ) { ret_y = y + h; } else { y -= h; ret_y = y; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { hcolor[0] = 1.0f; hcolor[1] = 0.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) hcolor[0] = 0.0f; hcolor[1] = 0.0f; hcolor[2] = 1.0f; hcolor[3] = 0.33f; } trap_R_SetColor( hcolor ); CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0; xx = x + TINYCHAR_WIDTH; CG_DrawStringExt( xx, y, ci->name, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH); if (lwidth) { p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) p = "unknown"; len = CG_DrawStrlen(p); if (len > lwidth) len = lwidth; // xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + // ((lwidth/2 - len/2) * TINYCHAR_WIDTH); xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth; CG_DrawStringExt( xx, y, p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXLOCATION_WIDTH); } CG_GetColorForHealth( ci->health, ci->armor, hcolor ); Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor); xx = x + TINYCHAR_WIDTH * 3 + TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth; CG_DrawStringExt( xx, y, st, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); // draw weapon icon xx += TINYCHAR_WIDTH * 3; if ( cg_weapons[ci->curWeapon].weaponIcon ) { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cg_weapons[ci->curWeapon].weaponIcon ); } else { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cgs.media.deferShader ); } // Draw powerup icons if (right) { xx = x; } else { xx = x + w - TINYCHAR_WIDTH; } for (j = 0; j <= PW_NUM_POWERUPS; j++) { if (ci->powerups & (1 << j)) { item = BG_FindItemForPowerup( j ); if (item) { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, trap_R_RegisterShader( item->icon ) ); if (right) { xx -= TINYCHAR_WIDTH; } else { xx += TINYCHAR_WIDTH; } } } } y += TINYCHAR_HEIGHT; } } return ret_y; //#endif } static float CG_DrawFollowMessage( float y ) { char *s; int w; if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) || ( ( cgs.elimflags & EF_NO_FREESPEC ) && (cgs.gametype == GT_ELIMINATION || cgs.gametype == GT_CTF_ELIMINATION ) ) ) { return y; } s = va("USE_ITEM to stop following"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallString( 635 - w, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT+4; } /* ===================== CG_DrawUpperRight ===================== */ static void CG_DrawUpperRight(stereoFrame_t stereoFrame) { float y; y = 0; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1 && cg_drawTeamOverlay.integer == 1 ) { y = CG_DrawTeamOverlay( y, qtrue, qtrue ); } /*if ( cgs.gametype == GT_DOUBLE_D ) { y = CG_DrawDoubleDominationThings(y); } else*/ if ( cgs.gametype == GT_LMS && cg.showScores ) { y = CG_DrawLMSmode(y); } else if ( cgs.gametype == GT_CTF_ELIMINATION ) { y = CG_DrawCTFoneway(y); } else if ( cgs.gametype == GT_DOMINATION ) { y = CG_DrawDomStatus(y); } if ( cg_drawSnapshot.integer ) { y = CG_DrawSnapshot( y ); } if (cg_drawFPS.integer && (stereoFrame == STEREO_CENTER || stereoFrame == STEREO_RIGHT)) { y = CG_DrawFPS( y ); } if (cgs.gametype==GT_ELIMINATION || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype==GT_LMS) { y = CG_DrawEliminationTimer( y ); /*if (cgs.clientinfo[ cg.clientNum ].isDead) y = CG_DrawEliminationDeathMessage( y);*/ } y = CG_DrawFollowMessage( y ); if ( cg_drawTimer.integer) { y = CG_DrawTimer( y ); } if ( cg_drawAttacker.integer ) { y = CG_DrawAttacker( y ); } if ( cg_drawSpeed.integer ) { y = CG_DrawSpeedMeter( y ); } } /* =========================================================================================== LOWER RIGHT CORNER =========================================================================================== */ /* ================= CG_DrawScores Draw the small two score display ================= */ #ifndef MISSIONPACK static float CG_DrawScores( float y ) { const char *s; int s1, s2, score; int x, w; int v; vec4_t color; float y1; gitem_t *item; int statusA,statusB; statusA = cgs.redflag; statusB = cgs.blueflag; s1 = cgs.scores1; s2 = cgs.scores2; y -= BIGCHAR_HEIGHT + 8; y1 = y; // draw from the right side to left if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { x = 640; color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 0.33f; s = va( "%2i", s2 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } CG_DrawBigString( x + 4, y, s, 1.0F); if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION) { // Display flag status item = BG_FindItemForPowerup( PW_BLUEFLAG ); if (item) { y1 = y - BIGCHAR_HEIGHT - 8; if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) { CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] ); } } } if ( cgs.gametype == GT_DOUBLE_D ) { // Display Domination point status y1 = y - 32;//BIGCHAR_HEIGHT - 8; if( cgs.redflag >= 0 && cgs.redflag <= 3 ) { CG_DrawPic( x, y1-4, w, 32, cgs.media.ddPointSkinB[cgs.blueflag] ); } } color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.33f; s = va( "%2i", s1 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } CG_DrawBigString( x + 4, y, s, 1.0F); if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION ) { // Display flag status item = BG_FindItemForPowerup( PW_REDFLAG ); if (item) { y1 = y - BIGCHAR_HEIGHT - 8; if( cgs.redflag >= 0 && cgs.redflag <= 2 ) { CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] ); } } } if ( cgs.gametype == GT_DOUBLE_D ) { // Display Domination point status y1 = y - 32;//BIGCHAR_HEIGHT - 8; if( cgs.redflag >= 0 && cgs.redflag <= 3 ) { CG_DrawPic( x, y1-4, w, 32, cgs.media.ddPointSkinA[cgs.redflag] ); } //Time till capture: if( ( ( statusB == statusA ) && ( statusA == TEAM_RED ) ) || ( ( statusB == statusA ) && ( statusA == TEAM_BLUE ) ) ) { s = va("%i",(cgs.timetaken+10*1000-cg.time)/1000+1); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( x + 32+8-w/2, y-28, s, 1.0F); } } if ( cgs.gametype == GT_OBELISK ) { s = va("^1%3i%% ^4%3i%%",cg.redObeliskHealth,cg.blueObeliskHealth); CG_DrawSmallString( x, y-28, s, 1.0F); } if ( cgs.gametype >= GT_CTF && cgs.ffa_gt==0) { v = cgs.capturelimit; } else { v = cgs.fraglimit; } if ( v ) { s = va( "%2i", v ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 4, y, s, 1.0F); } } else { qboolean spectator; x = 640; score = cg.snap->ps.persistant[PERS_SCORE]; spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ); // always show your score in the second box if not in first place if ( s1 != score ) { s2 = score; } if ( s2 != SCORE_NOT_PRESENT ) { s = va( "%2i", s2 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; if ( !spectator && score == s2 && score != s1 ) { color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } else { color[0] = 0.5f; color[1] = 0.5f; color[2] = 0.5f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); } CG_DrawBigString( x + 4, y, s, 1.0F); } // first place if ( s1 != SCORE_NOT_PRESENT ) { s = va( "%2i", s1 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; if ( !spectator && score == s1 ) { color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } else { color[0] = 0.5f; color[1] = 0.5f; color[2] = 0.5f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); } CG_DrawBigString( x + 4, y, s, 1.0F); } if ( cgs.fraglimit ) { s = va( "%2i", cgs.fraglimit ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 4, y, s, 1.0F); } } return y1 - 8; } #endif // MISSIONPACK /* ================ CG_DrawPowerups ================ */ #ifndef MISSIONPACK static float CG_DrawPowerups( float y ) { int sorted[MAX_POWERUPS]; int sortedTime[MAX_POWERUPS]; int i, j, k; int active; playerState_t *ps; int t; gitem_t *item; int x; int color; float size; float f; static float colors[2][4] = { { 0.2f, 1.0f, 0.2f, 1.0f } , { 1.0f, 0.2f, 0.2f, 1.0f } }; ps = &cg.snap->ps; if ( ps->stats[STAT_HEALTH] <= 0 ) { return y; } // sort the list by time remaining active = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( !ps->powerups[ i ] ) { continue; } t = ps->powerups[ i ] - cg.time; // ZOID--don't draw if the power up has unlimited time (999 seconds) // This is true of the CTF flags if ( t < 0 || t > 999000) { continue; } item = BG_FindItemForPowerup( i ); if ( item && item->giType == IT_PERSISTANT_POWERUP) continue; //Don't draw persistant powerups here! // insert into the list for ( j = 0 ; j < active ; j++ ) { if ( sortedTime[j] >= t ) { for ( k = active - 1 ; k >= j ; k-- ) { sorted[k+1] = sorted[k]; sortedTime[k+1] = sortedTime[k]; } break; } } sorted[j] = i; sortedTime[j] = t; active++; } // draw the icons and timers x = 640 - ICON_SIZE - CHAR_WIDTH * 2; for ( i = 0 ; i < active ; i++ ) { item = BG_FindItemForPowerup( sorted[i] ); if (item) { color = 1; y -= ICON_SIZE; trap_R_SetColor( colors[color] ); CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 ); t = ps->powerups[ sorted[i] ]; if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { trap_R_SetColor( NULL ); } else { vec4_t modulate; f = (float)( t - cg.time ) / POWERUP_BLINK_TIME; f -= (int)f; modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; trap_R_SetColor( modulate ); } if ( cg.powerupActive == sorted[i] && cg.time - cg.powerupTime < PULSE_TIME ) { f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME ); size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f ); } else { size = ICON_SIZE; } CG_DrawPic( 640 - size, y + ICON_SIZE / 2 - size / 2, size, size, trap_R_RegisterShader( item->icon ) ); } } trap_R_SetColor( NULL ); return y; } #endif // MISSIONPACK /* ===================== CG_DrawLowerRight ===================== */ #ifndef MISSIONPACK static void CG_DrawLowerRight( void ) { float y; y = 480 - ICON_SIZE; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1 && cg_drawTeamOverlay.integer == 2 ) { y = CG_DrawTeamOverlay( y, qtrue, qfalse ); } y = CG_DrawScores( y ); y = CG_DrawPowerups( y ); } #endif // MISSIONPACK /* =================== CG_DrawPickupItem =================== */ #ifndef MISSIONPACK static int CG_DrawPickupItem( int y ) { int value; float *fadeColor; if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { return y; } y -= ICON_SIZE; value = cg.itemPickup; if ( value ) { fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 ); if ( fadeColor ) { CG_RegisterItemVisuals( value ); trap_R_SetColor( fadeColor ); CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] ); trap_R_SetColor( NULL ); } } return y; } #endif // MISSIONPACK /* ===================== CG_DrawLowerLeft ===================== */ #ifndef MISSIONPACK static void CG_DrawLowerLeft( void ) { float y; y = 480 - ICON_SIZE; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1 && cg_drawTeamOverlay.integer == 3 ) { y = CG_DrawTeamOverlay( y, qfalse, qfalse ); } y = CG_DrawPickupItem( y ); } #endif // MISSIONPACK //=========================================================================================== /* ================= CG_DrawTeamInfo ================= */ #ifndef MISSIONPACK static void CG_DrawTeamInfo( void ) { int w, h; int i, len; vec4_t hcolor; int chatHeight; #define CHATLOC_Y 420 // bottom end #define CHATLOC_X 0 if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) chatHeight = cg_teamChatHeight.integer; else chatHeight = TEAMCHAT_HEIGHT; if (chatHeight <= 0) return; // disabled if (cgs.teamLastChatPos != cgs.teamChatPos) { if (cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer) { cgs.teamLastChatPos++; } h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT; w = 0; for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) { len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]); if (len > w) w = len; } w *= TINYCHAR_WIDTH; w += TINYCHAR_WIDTH * 2; if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { hcolor[0] = 1.0f; hcolor[1] = 0.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { hcolor[0] = 0.0f; hcolor[1] = 0.0f; hcolor[2] = 1.0f; hcolor[3] = 0.33f; } else { hcolor[0] = 0.0f; hcolor[1] = 1.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } trap_R_SetColor( hcolor ); CG_DrawPic( CHATLOC_X, CHATLOC_Y - h, 640, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); hcolor[0] = hcolor[1] = hcolor[2] = 1.0f; hcolor[3] = 1.0f; for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) { CG_DrawStringExt( CHATLOC_X + TINYCHAR_WIDTH, CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT, cgs.teamChatMsgs[i % chatHeight], hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); } } } #endif // MISSIONPACK /* =================== CG_DrawHoldableItem =================== */ #ifndef MISSIONPACK static void CG_DrawHoldableItem( void ) { int value; value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM]; if ( value ) { CG_RegisterItemVisuals( value ); CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); } } #endif // MISSIONPACK #ifndef MISSIONPACK /* =================== CG_DrawPersistantPowerup =================== */ #if 1 // sos001208 - DEAD // sago - ALIVE static void CG_DrawPersistantPowerup( void ) { int value; value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP]; if ( value ) { CG_RegisterItemVisuals( value ); CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2 - ICON_SIZE, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); } } #endif #endif // MISSIONPACK /* =================== CG_DrawReward =================== */ static void CG_DrawReward( void ) { float *color; int i, count; float x, y; char buf[32]; if ( !cg_drawRewards.integer ) { return; } color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); if ( !color ) { if (cg.rewardStack > 0) { for(i = 0; i < cg.rewardStack; i++) { cg.rewardSound[i] = cg.rewardSound[i+1]; cg.rewardShader[i] = cg.rewardShader[i+1]; cg.rewardCount[i] = cg.rewardCount[i+1]; } cg.rewardTime = cg.time; cg.rewardStack--; color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER); } else { return; } } trap_R_SetColor( color ); /* count = cg.rewardCount[0]/10; // number of big rewards to draw if (count) { y = 4; x = 320 - count * ICON_SIZE; for ( i = 0 ; i < count ; i++ ) { CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] ); x += (ICON_SIZE*2); } } count = cg.rewardCount[0] - count*10; // number of small rewards to draw */ if ( cg.rewardCount[0] >= 10 ) { y = 56; x = 320 - ICON_SIZE/2; CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]); x = ( SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen( buf ) ) / 2; CG_DrawStringExt( x, y+ICON_SIZE, buf, color, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } else { count = cg.rewardCount[0]; y = 56; x = 320 - count * ICON_SIZE/2; for ( i = 0 ; i < count ; i++ ) { CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); x += ICON_SIZE; } } trap_R_SetColor( NULL ); } /* =============================================================================== LAGOMETER =============================================================================== */ #define LAG_SAMPLES 128 typedef struct { int frameSamples[LAG_SAMPLES]; int frameCount; int snapshotFlags[LAG_SAMPLES]; int snapshotSamples[LAG_SAMPLES]; int snapshotCount; } lagometer_t; lagometer_t lagometer; /* ============== CG_AddLagometerFrameInfo Adds the current interpolate / extrapolate bar for this frame ============== */ void CG_AddLagometerFrameInfo( void ) { int offset; offset = cg.time - cg.latestSnapshotTime; lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset; lagometer.frameCount++; } /* ============== CG_AddLagometerSnapshotInfo Each time a snapshot is received, log its ping time and the number of snapshots that were dropped before it. Pass NULL for a dropped packet. ============== */ void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { // dropped packet if ( !snap ) { lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1; lagometer.snapshotCount++; return; } // add this snapshot's info lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping; lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags; lagometer.snapshotCount++; } /* ============== CG_DrawDisconnect Should we draw something differnet for long lag vs no packets? ============== */ static void CG_DrawDisconnect( void ) { float x, y; int cmdNum; usercmd_t cmd; const char *s; int w; // bk010215 - FIXME char message[1024]; // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &cmd ); if ( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME return; } // also add text in center of screen s = "Connection Interrupted"; // bk 010215 - FIXME w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w/2, 100, s, 1.0F); // blink the icon if ( ( cg.time >> 9 ) & 1 ) { return; } x = 640 - 48; y = 480 - 48; CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) ); } #define MAX_LAGOMETER_PING 900 #define MAX_LAGOMETER_RANGE 300 /* ============== CG_DrawLagometer ============== */ static void CG_DrawLagometer( void ) { int a, x, y, i; float v; float ax, ay, aw, ah, mid, range; int color; float vscale; if ( !cg_lagometer.integer || cgs.localServer ) { CG_DrawDisconnect(); return; } // // draw the graph // #ifdef MISSIONPACK x = 640 - 48; y = 480 - 144; #else x = 640 - 48; y = 480 - 48; #endif trap_R_SetColor( NULL ); CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader ); ax = x; ay = y; aw = 48; ah = 48; CG_AdjustFrom640( &ax, &ay, &aw, &ah ); color = -1; range = ah / 3; mid = ay + range; vscale = range / MAX_LAGOMETER_RANGE; // draw the frame interpoalte / extrapolate graph for ( a = 0 ; a < aw ; a++ ) { i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1); v = lagometer.frameSamples[i]; v *= vscale; if ( v > 0 ) { if ( color != 1 ) { color = 1; trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); } if ( v > range ) { v = range; } trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if ( v < 0 ) { if ( color != 2 ) { color = 2; trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] ); } v = -v; if ( v > range ) { v = range; } trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } } // draw the snapshot latency / drop graph range = ah / 2; vscale = range / MAX_LAGOMETER_PING; for ( a = 0 ; a < aw ; a++ ) { i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1); v = lagometer.snapshotSamples[i]; if ( v > 0 ) { if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) { if ( color != 5 ) { color = 5; // YELLOW for rate delay trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); } } else { if ( color != 3 ) { color = 3; trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] ); } } v = v * vscale; if ( v > range ) { v = range; } trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if ( v < 0 ) { if ( color != 4 ) { color = 4; // RED for dropped snapshots trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] ); } trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); } } trap_R_SetColor( NULL ); if ( cg_nopredict.integer || cg_synchronousClients.integer ) { CG_DrawBigString( ax, ay, "snc", 1.0 ); } CG_DrawDisconnect(); } /* =============================================================================== CENTER PRINTING =============================================================================== */ /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) ); cg.centerPrintTime = cg.time; cg.centerPrintY = y; cg.centerPrintCharWidth = charWidth; // count the number of lines for centering cg.centerPrintLines = 1; s = cg.centerPrint; while( *s ) { if (*s == '\n') cg.centerPrintLines++; s++; } } /* =================== CG_DrawCenterString =================== */ static void CG_DrawCenterString( void ) { char *start; int l; int x, y, w; #ifdef MISSIONPACK // bk010221 - unused else int h; #endif float *color; if ( !cg.centerPrintTime ) { return; } color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); if ( !color ) { return; } trap_R_SetColor( color ); start = cg.centerPrint; y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2; while ( 1 ) { char linebuffer[1024]; for ( l = 0; l < 50; l++ ) { if ( !start[l] || start[l] == '\n' ) { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; #ifdef MISSIONPACK w = CG_Text_Width(linebuffer, 0.5, 0); h = CG_Text_Height(linebuffer, 0.5, 0); x = (SCREEN_WIDTH - w) / 2; CG_Text_Paint(x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); y += h + 6; #else w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer ); x = ( SCREEN_WIDTH - w ) / 2; CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 ); y += cg.centerPrintCharWidth * 1.5; #endif while ( *start && ( *start != '\n' ) ) { start++; } if ( !*start ) { break; } start++; } trap_R_SetColor( NULL ); } /* ===================== CG_DrawCenter1FctfString ===================== */ static void CG_DrawCenter1FctfString( void ) { #ifndef MISSIONPACK int x, y, w; float *color; char *line; int status; if(cgs.gametype != GT_1FCTF) return; status = cgs.flagStatus; //Sago: TODO: Find the proper defines instead of hardcoded values. switch(status) { case 2: line = va("Red has the flag!"); color = colorRed; break; case 3: line = va("Blue has the flag!"); color = colorBlue; break; case 4: line = va("Flag dropped!"); color = colorWhite; break; default: return; }; y = 100; w = cg.centerPrintCharWidth * CG_DrawStrlen( line ); x = ( SCREEN_WIDTH - w ) / 2; CG_DrawStringExt( x, y, line, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 ); #endif } /* ===================== CG_DrawCenterDDString ===================== */ static void CG_DrawCenterDDString( void ) { #ifndef MISSIONPACK int x, y, w; float *color; char *line; int statusA, statusB; int sec; static int lastDDSec = -100; if(cgs.gametype != GT_DOUBLE_D) return; statusA = cgs.redflag; statusB = cgs.blueflag; if( ( ( statusB == statusA ) && ( statusA == TEAM_RED ) ) || ( ( statusB == statusA ) && ( statusA == TEAM_BLUE ) ) ) { } else return; //No team is dominating if(statusA == TEAM_BLUE) { line = va("Blue scores in %i",(cgs.timetaken+10*1000-cg.time)/1000+1); color = colorBlue; } else if(statusA == TEAM_RED) { line = va("Red scores in %i",(cgs.timetaken+10*1000-cg.time)/1000+1); color = colorRed; } else { lastDDSec = -100; return; } sec = (cgs.timetaken+10*1000-cg.time)/1000+1; if(sec!=lastDDSec) { //A new number is being displayed... play the sound! switch ( sec ) { case 1: trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); break; case 2: trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); break; case 3: trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); break; case 10: trap_S_StartLocalSound( cgs.media.doublerSound , CHAN_ANNOUNCER ); break; default: break; } } lastDDSec = sec; y = 100; w = cg.centerPrintCharWidth * CG_DrawStrlen( line ); x = ( SCREEN_WIDTH - w ) / 2; CG_DrawStringExt( x, y, line, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 ); #endif } /* ================================================================================ CROSSHAIR ================================================================================ */ /* ================= CG_DrawCrosshair ================= */ static void CG_DrawCrosshair(void) { float w, h; qhandle_t hShader; float f; float x, y; int ca = 0; //only to get rid of the warning(not useful) int currentWeapon; currentWeapon = cg.predictedPlayerState.weapon; if ( !cg_drawCrosshair.integer ) { return; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { return; } if ( cg.renderingThirdPerson ) { return; } // set color based on health if ( cg_crosshairHealth.integer ) { vec4_t hcolor; CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); } else { vec4_t color; color[0]=cg_crosshairColorRed.value; color[1]=cg_crosshairColorGreen.value; color[2]=cg_crosshairColorBlue.value; color[3]=1.0f; trap_R_SetColor( color ); } if( cg_differentCrosshairs.integer == 1 ){ switch( currentWeapon ){ case 1: w = h = cg_ch1size.value; ca = cg_ch1.integer; break; case 2: w = h = cg_ch2size.value; ca = cg_ch2.integer; break; case 3: w = h = cg_ch3size.value; ca = cg_ch3.integer; break; case 4: w = h = cg_ch4size.value; ca = cg_ch4.integer; break; case 5: w = h = cg_ch5size.value; ca = cg_ch5.integer; break; case 6: w = h = cg_ch6size.value; ca = cg_ch6.integer; break; case 7: w = h = cg_ch7size.value; ca = cg_ch7.integer; break; case 8: w = h = cg_ch8size.value; ca = cg_ch8.integer; break; case 9: w = h = cg_ch9size.value; ca = cg_ch9.integer; break; case 10: w = h = cg_ch10size.value; ca = cg_ch10.integer; break; case 11: w = h = cg_ch11size.value; ca = cg_ch11.integer; break; case 12: w = h = cg_ch12size.value; ca = cg_ch12.integer; break; case 13: w = h = cg_ch13size.value; ca = cg_ch13.integer; break; default: w = h = cg_crosshairSize.value; ca = cg_drawCrosshair.integer; break; } } else{ w = h = cg_crosshairSize.value; ca = cg_drawCrosshair.integer; } if( cg_crosshairPulse.integer ){ // pulse the size of the crosshair when picking up items f = cg.time - cg.itemPickupBlendTime; if ( f > 0 && f < ITEM_BLOB_TIME ) { f /= ITEM_BLOB_TIME; w *= ( 1 + f ); h *= ( 1 + f ); } } x = cg_crosshairX.integer; y = cg_crosshairY.integer; CG_AdjustFrom640( &x, &y, &w, &h ); if (ca < 0) { ca = 0; } hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; if(!hShader) hShader = cgs.media.crosshairShader[ ca % 10 ]; trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w), y + cg.refdef.y + 0.5 * (cg.refdef.height - h), w, h, 0, 0, 1, 1, hShader ); } /* ================= CG_DrawCrosshair3D ================= */ static void CG_DrawCrosshair3D(void) { float w, h; qhandle_t hShader; float f; int ca; trace_t trace; vec3_t endpos; float stereoSep, zProj, maxdist, xmax; char rendererinfos[128]; refEntity_t ent; if ( !cg_drawCrosshair.integer ) { return; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { return; } if ( cg.renderingThirdPerson ) { return; } w = h = cg_crosshairSize.value; // pulse the size of the crosshair when picking up items f = cg.time - cg.itemPickupBlendTime; if ( f > 0 && f < ITEM_BLOB_TIME ) { f /= ITEM_BLOB_TIME; w *= ( 1 + f ); h *= ( 1 + f ); } ca = cg_drawCrosshair.integer; if (ca < 0) { ca = 0; } hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; if(!hShader) hShader = cgs.media.crosshairShader[ ca % 10 ]; // Use a different method rendering the crosshair so players don't see two of them when // focusing their eyes at distant objects with high stereo separation // We are going to trace to the next shootable object and place the crosshair in front of it. // first get all the important renderer information trap_Cvar_VariableStringBuffer("r_zProj", rendererinfos, sizeof(rendererinfos)); zProj = atof(rendererinfos); trap_Cvar_VariableStringBuffer("r_stereoSeparation", rendererinfos, sizeof(rendererinfos)); stereoSep = zProj / atof(rendererinfos); xmax = zProj * tan(cg.refdef.fov_x * M_PI / 360.0f); // let the trace run through until a change in stereo separation of the crosshair becomes less than one pixel. maxdist = cgs.glconfig.vidWidth * stereoSep * zProj / (2 * xmax); VectorMA(cg.refdef.vieworg, maxdist, cg.refdef.viewaxis[0], endpos); CG_Trace(&trace, cg.refdef.vieworg, NULL, NULL, endpos, 0, MASK_SHOT); memset(&ent, 0, sizeof(ent)); ent.reType = RT_SPRITE; ent.renderfx = RF_DEPTHHACK | RF_CROSSHAIR; VectorCopy(trace.endpos, ent.origin); // scale the crosshair so it appears the same size for all distances ent.radius = w / 640 * xmax * trace.fraction * maxdist / zProj; ent.customShader = hShader; trap_R_AddRefEntityToScene(&ent); } /* ================= CG_ScanForCrosshairEntity ================= */ static void CG_ScanForCrosshairEntity( void ) { trace_t trace; vec3_t start, end; int content; VectorCopy( cg.refdef.vieworg, start ); VectorMA( start, 131072, cg.refdef.viewaxis[0], end ); CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY ); if ( trace.entityNum >= MAX_CLIENTS ) { return; } // if the player is in fog, don't show it content = CG_PointContents( trace.endpos, 0 ); if ( content & CONTENTS_FOG ) { return; } // if the player is invisible, don't show it if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) { return; } // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; } /* ===================== CG_DrawCrosshairNames ===================== */ static void CG_DrawCrosshairNames( void ) { float *color; char *name; float w; if ( !cg_drawCrosshair.integer ) { return; } if ( !cg_drawCrosshairNames.integer ) { return; } if ( cg.renderingThirdPerson ) { return; } // scan the known entities to see if the crosshair is sighted on one CG_ScanForCrosshairEntity(); // draw the name of the player being looked at color = CG_FadeColor( cg.crosshairClientTime, 1000 ); if ( !color ) { trap_R_SetColor( NULL ); return; } name = cgs.clientinfo[ cg.crosshairClientNum ].name; #ifdef MISSIONPACK color[3] *= 0.5f; w = CG_Text_Width(name, 0.3f, 0); CG_Text_Paint( 320 - w / 2, 190, 0.3f, color, name, 0, 0, ITEM_TEXTSTYLE_SHADOWED); #else w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5f ); #endif trap_R_SetColor( NULL ); } //============================================================================== /* ================= CG_DrawSpectator ================= */ static void CG_DrawSpectator(void) { CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F); if ( cgs.gametype == GT_TOURNAMENT ) { CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F); } else if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the JOIN menu to play", 1.0F); } } /* ================= CG_DrawVote ================= */ static void CG_DrawVote(void) { char *s; int sec; if ( !cgs.voteTime ) { return; } // play a talk beep whenever it is modified if ( cgs.voteModified ) { cgs.voteModified = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; if ( sec < 0 ) { sec = 0; } #ifdef MISSIONPACK s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo); CG_DrawSmallString( 0, 58, s, 1.0F ); s = "or press ESC then click Vote"; CG_DrawSmallString( 0, 58 + SMALLCHAR_HEIGHT + 2, s, 1.0F ); #else s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo ); CG_DrawSmallString( 0, 58, s, 1.0F ); #endif } /* ================= CG_DrawTeamVote ================= */ static void CG_DrawTeamVote(void) { char *s; int sec, cs_offset; if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) cs_offset = 0; else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) cs_offset = 1; else return; if ( !cgs.teamVoteTime[cs_offset] ) { return; } // play a talk beep whenever it is modified if ( cgs.teamVoteModified[cs_offset] ) { cgs.teamVoteModified[cs_offset] = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[cs_offset] ) ) / 1000; if ( sec < 0 ) { sec = 0; } s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); CG_DrawSmallString( 0, 90, s, 1.0F ); } static qboolean CG_DrawScoreboard( void ) { #ifdef MISSIONPACK static qboolean firstTime = qtrue; float fade, *fadeColor; if (menuScoreboard) { menuScoreboard->window.flags &= ~WINDOW_FORCED; } if (cg_paused.integer) { cg.deferredPlayerLoading = 0; firstTime = qtrue; return qfalse; } // should never happen in Team Arena if (cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { cg.deferredPlayerLoading = 0; firstTime = qtrue; return qfalse; } // don't draw scoreboard during death while warmup up if ( cg.warmup && !cg.showScores ) { return qfalse; } if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { fade = 1.0; fadeColor = colorWhite; } else { fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); if ( !fadeColor ) { // next time scoreboard comes up, don't print killer cg.deferredPlayerLoading = 0; cg.killerName[0] = 0; firstTime = qtrue; return qfalse; } fade = *fadeColor; } if (menuScoreboard == NULL) { if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { menuScoreboard = Menus_FindByName("teamscore_menu"); } else { menuScoreboard = Menus_FindByName("score_menu"); } } if (menuScoreboard) { if (firstTime) { CG_SetScoreSelection(menuScoreboard); firstTime = qfalse; } Menu_Paint(menuScoreboard, qtrue); } // load any models that have been deferred if ( ++cg.deferredPlayerLoading > 10 ) { CG_LoadDeferredPlayers(); } return qtrue; #else char *s; int w; if(cg.respawnTime && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR && (cgs.gametype < GT_ELIMINATION || cgs.gametype > GT_LMS) ) { if(cg.respawnTime>cg.time) { s = va("Respawn in: %2.2f",((double)cg.respawnTime-(double)cg.time)/1000.0); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(320-w/2,400, s, colorYellow); } else { s = va("Click FIRE to respawn"); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(320-w/2,400, "Click FIRE to respawn", colorGreen); } } return CG_DrawOldScoreboard(); #endif } #define ACCBOARD_XPOS 500 #define ACCBOARD_YPOS 150 #define ACCBOARD_HEIGHT 20 #define ACCBOARD_WIDTH 75 #define ACCITEM_SIZE 16 qboolean CG_DrawAccboard( void ) { int counter, i; i = 0; if( !cg.showAcc ){ return qfalse; } trap_R_SetColor( colorWhite ); for( counter = 0; counter < WP_NUM_WEAPONS ; counter++ ){ if( cg_weapons[counter+2].weaponIcon && counter != WP_PROX_LAUNCHER && counter != WP_GRAPPLING_HOOK ) i++; } CG_DrawTeamBackground( ACCBOARD_XPOS, ACCBOARD_YPOS, ACCBOARD_WIDTH, ACCBOARD_HEIGHT*(i + 1), 0.33f, TEAM_BLUE ); i = 0; for( counter = 0 ; counter < WP_NUM_WEAPONS ; counter++ ){ if( cg_weapons[counter+2].weaponIcon && counter != WP_PROX_LAUNCHER && counter != WP_GRAPPLING_HOOK ){ CG_DrawPic( ACCBOARD_XPOS + 10, ACCBOARD_YPOS + 10 +i*ACCBOARD_HEIGHT, ACCITEM_SIZE, ACCITEM_SIZE, cg_weapons[counter+2].weaponIcon ); if( cg.accuracys[counter][0] > 0 ) CG_DrawSmallStringColor(ACCBOARD_XPOS + 10 + ACCITEM_SIZE + 10, ACCBOARD_YPOS + 10 +i*ACCBOARD_HEIGHT + ACCITEM_SIZE/2 - SMALLCHAR_HEIGHT/2 , va("%i%s",(int)(((float)cg.accuracys[counter][1]*100)/((float)(cg.accuracys[counter][0]))),"%"), colorWhite); else CG_DrawSmallStringColor(ACCBOARD_XPOS + 10 + ACCITEM_SIZE + 10, ACCBOARD_YPOS + 10 +i*ACCBOARD_HEIGHT + ACCITEM_SIZE/2 - SMALLCHAR_HEIGHT/2 , "-%", colorWhite); i++; } } trap_R_SetColor(NULL); return qtrue; } /* ================= CG_DrawIntermission ================= */ static void CG_DrawIntermission( void ) { // int key; #ifdef MISSIONPACK //if (cg_singlePlayer.integer) { // CG_DrawCenterString(); // return; //} #else if ( cgs.gametype == GT_SINGLE_PLAYER ) { CG_DrawCenterString(); return; } #endif cg.scoreFadeTime = cg.time; cg.scoreBoardShowing = CG_DrawScoreboard(); } /* ================= CG_DrawFollow ================= */ static qboolean CG_DrawFollow( void ) { float x; vec4_t color; const char *name; if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) { return qfalse; } color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F ); name = cgs.clientinfo[ cg.snap->ps.clientNum ].name; x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) ); CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); return qtrue; } /* ================= CG_DrawAmmoWarning ================= */ static void CG_DrawAmmoWarning( void ) { const char *s; int w; //Don't report in instant gib same with RA if(cgs.nopickup) return; if ( cg_drawAmmoWarning.integer == 0 ) { return; } if ( !cg.lowAmmoWarning ) { return; } if ( cg.lowAmmoWarning == 2 ) { s = "OUT OF AMMO"; } else { s = "LOW AMMO WARNING"; } w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 64, s, 1.0F); } //#ifdef MISSIONPACK /* ================= CG_DrawProxWarning ================= */ static void CG_DrawProxWarning( void ) { char s [32]; int w; static int proxTime; static int proxCounter; static int proxTick; if( !(cg.snap->ps.eFlags & EF_TICKING ) ) { proxTime = 0; return; } if (proxTime == 0) { proxTime = cg.time + 5000; proxCounter = 5; proxTick = 0; } if (cg.time > proxTime) { proxTick = proxCounter--; proxTime = cg.time + 1000; } if (proxTick != 0) { Com_sprintf(s, sizeof(s), "INTERNAL COMBUSTION IN: %i", proxTick); } else { Com_sprintf(s, sizeof(s), "YOU HAVE BEEN MINED"); } w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigStringColor( 320 - w / 2, 64 + BIGCHAR_HEIGHT, s, g_color_table[ColorIndex(COLOR_RED)] ); } //#endif /* ================= CG_DrawWarmup ================= */ static void CG_DrawWarmup( void ) { int w; int sec; int i; float scale; clientInfo_t *ci1, *ci2; int cw; const char *s; sec = cg.warmup; if ( !sec ) { return; } if ( sec < 0 ) { s = "Waiting for players"; w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 24, s, 1.0F); cg.warmupCount = 0; return; } if (cgs.gametype == GT_TOURNAMENT) { // find the two active players ci1 = NULL; ci2 = NULL; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) { if ( !ci1 ) { ci1 = &cgs.clientinfo[i]; } else { ci2 = &cgs.clientinfo[i]; } } } if ( ci1 && ci2 ) { s = va( "%s vs %s", ci1->name, ci2->name ); #ifdef MISSIONPACK w = CG_Text_Width(s, 0.6f, 0); CG_Text_Paint(320 - w / 2, 60, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); #else w = CG_DrawStrlen( s ); if ( w > 640 / GIANT_WIDTH ) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5f), 0 ); #endif } } else { if ( cgs.gametype == GT_FFA ) { s = "Free For All"; } else if ( cgs.gametype == GT_TEAM ) { s = "Team Deathmatch"; } else if ( cgs.gametype == GT_CTF ) { s = "Capture the Flag"; } else if ( cgs.gametype == GT_ELIMINATION ) { s = "Elimination"; } else if ( cgs.gametype == GT_CTF_ELIMINATION ) { s = "CTF Elimination"; } else if ( cgs.gametype == GT_LMS ) { s = "Last Man Standing"; } else if ( cgs.gametype == GT_DOUBLE_D ) { s = "Double Domination"; } else if ( cgs.gametype == GT_1FCTF ) { s = "One Flag CTF"; } else if ( cgs.gametype == GT_OBELISK ) { s = "Overload"; } else if ( cgs.gametype == GT_HARVESTER ) { s = "Harvester"; } else if ( cgs.gametype == GT_DOMINATION ) { s = "Domination"; } else { s = ""; } #ifdef MISSIONPACK w = CG_Text_Width(s, 0.6f, 0); CG_Text_Paint(320 - w / 2, 90, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); #else w = CG_DrawStrlen( s ); if ( w > 640 / GIANT_WIDTH ) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt( 320 - w * cw/2, 25,s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.1f), 0 ); #endif } sec = ( sec - cg.time ) / 1000; if ( sec < 0 ) { cg.warmup = 0; sec = 0; } s = va( "Starts in: %i", sec + 1 ); if ( sec != cg.warmupCount ) { cg.warmupCount = sec; switch ( sec ) { case 0: trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); break; case 1: trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); break; case 2: trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); break; default: break; } } scale = 0.45f; switch ( cg.warmupCount ) { case 0: cw = 28; scale = 0.54f; break; case 1: cw = 24; scale = 0.51f; break; case 2: cw = 20; scale = 0.48f; break; default: cw = 16; scale = 0.45f; break; } #ifdef MISSIONPACK w = CG_Text_Width(s, scale, 0); CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); #else w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); #endif } //================================================================================== #ifdef MISSIONPACK /* ================= CG_DrawTimedMenus ================= */ void CG_DrawTimedMenus( void ) { if (cg.voiceTime) { int t = cg.time - cg.voiceTime; if ( t > 2500 ) { Menus_CloseByName("voiceMenu"); trap_Cvar_Set("cl_conXOffset", "0"); cg.voiceTime = 0; } } } #endif /* ================= CG_Draw2D ================= */ static void CG_Draw2D(stereoFrame_t stereoFrame) { #ifdef MISSIONPACK if (cgs.orderPending && cg.time > cgs.orderTime) { CG_CheckOrderPending(); } #endif // if we are taking a levelshot for the menu, don't draw anything if ( cg.levelShot ) { return; } if ( cg_draw2D.integer == 0 ) { return; } if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { CG_DrawIntermission(); return; } /* if (cg.cameraMode) { return; } */ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR /*|| cg.snap->ps.pm_type == PM_SPECTATOR*/ ) { CG_DrawSpectator(); if(stereoFrame == STEREO_CENTER) CG_DrawCrosshair(); CG_DrawCrosshairNames(); } else { // don't draw any status if dead or the scoreboard is being explicitly shown if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) { #ifdef MISSIONPACK if ( cg_drawStatus.integer ) { Menu_PaintAll(); CG_DrawTimedMenus(); } #else CG_DrawStatusBar(); #endif CG_DrawAmmoWarning(); CG_DrawProxWarning(); if(stereoFrame == STEREO_CENTER) CG_DrawCrosshair(); CG_DrawCrosshairNames(); CG_DrawWeaponSelect(); #ifndef MISSIONPACK CG_DrawHoldableItem(); CG_DrawPersistantPowerup(); #endif CG_DrawReward(); } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { #ifndef MISSIONPACK CG_DrawTeamInfo(); #endif } } CG_DrawVote(); CG_DrawTeamVote(); CG_DrawLagometer(); #ifdef MISSIONPACK if (!cg_paused.integer) { CG_DrawUpperRight(stereoFrame); } #else CG_DrawUpperRight(stereoFrame); #endif #ifndef MISSIONPACK CG_DrawLowerRight(); CG_DrawLowerLeft(); #endif if ( !CG_DrawFollow() ) { CG_DrawWarmup(); } // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard(); if ( !cg.scoreBoardShowing) { CG_DrawCenterDDString(); CG_DrawCenter1FctfString(); CG_DrawCenterString(); } cg.accBoardShowing = CG_DrawAccboard(); } static void CG_DrawTourneyScoreboard( void ) { #ifdef MISSIONPACK #else CG_DrawOldTourneyScoreboard(); #endif } /* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive( stereoFrame_t stereoView ) { // optionally draw the info screen instead if ( !cg.snap ) { CG_DrawInformation(); return; } // optionally draw the tournement scoreboard instead if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) { CG_DrawTourneyScoreboard(); return; } // clear around the rendered view if sized down CG_TileClear(); if(stereoView != STEREO_CENTER) CG_DrawCrosshair3D(); // draw 3D view trap_R_RenderScene( &cg.refdef ); // draw status bar and other floating elements CG_Draw2D(stereoView); } openarena_0.8.8.orig/code/cgame/cg_event.c0000644000175000017500000011443411656310265017162 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_event.c -- handle entity events at snapshot or playerstate transitions #include "cg_local.h" // for the voice chats #ifdef MISSIONPACK // bk001205 #include "../../ui/menudef.h" #endif //========================================================================== /* =================== CG_PlaceString Also called by scoreboard drawing =================== */ const char *CG_PlaceString( int rank ) { static char str[64]; char *s, *t; if ( rank & RANK_TIED_FLAG ) { rank &= ~RANK_TIED_FLAG; t = "Tied for "; } else { t = ""; } if ( rank == 1 ) { s = S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue } else if ( rank == 2 ) { s = S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red } else if ( rank == 3 ) { s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow } else if ( rank == 11 ) { s = "11th"; } else if ( rank == 12 ) { s = "12th"; } else if ( rank == 13 ) { s = "13th"; } else if ( rank % 10 == 1 ) { s = va("%ist", rank); } else if ( rank % 10 == 2 ) { s = va("%ind", rank); } else if ( rank % 10 == 3 ) { s = va("%ird", rank); } else { s = va("%ith", rank); } Com_sprintf( str, sizeof( str ), "%s%s", t, s ); return str; } /* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; char *message; char *message2; const char *targetInfo; const char *attackerInfo; char targetName[32]; char attackerName[32]; gender_t gender; clientInfo_t *ci; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[target]; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { attacker = ENTITYNUM_WORLD; attackerInfo = NULL; } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); strcat( targetName, S_COLOR_WHITE ); message2 = ""; // check for single client messages if(attacker != ENTITYNUM_WORLD) message = NULL; else switch( mod ) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; default: message = NULL; break; } if (attacker == target) { gender = ci->gender; switch (mod) { case MOD_KAMIKAZE: message = "goes out with a bang"; break; case MOD_GRENADE_SPLASH: if ( gender == GENDER_FEMALE ) message = "tripped on her own grenade"; else if ( gender == GENDER_NEUTER ) message = "tripped on its own grenade"; else message = "tripped on his own grenade"; break; case MOD_ROCKET_SPLASH: if ( gender == GENDER_FEMALE ) message = "blew herself up"; else if ( gender == GENDER_NEUTER ) message = "blew itself up"; else message = "blew himself up"; break; case MOD_PLASMA_SPLASH: if ( gender == GENDER_FEMALE ) message = "melted herself"; else if ( gender == GENDER_NEUTER ) message = "melted itself"; else message = "melted himself"; break; case MOD_BFG_SPLASH: message = "should have used a smaller gun"; break; case MOD_PROXIMITY_MINE: if( gender == GENDER_FEMALE ) { message = "found her prox mine"; } else if ( gender == GENDER_NEUTER ) { message = "found its prox mine"; } else { message = "found his prox mine"; } break; default: if ( gender == GENDER_FEMALE ) message = "killed herself"; else if ( gender == GENDER_NEUTER ) message = "killed itself"; else message = "killed himself"; break; } } //If a suicide happens while disconnecting then we might not have a targetName if (message && strlen(targetName)) { CG_Printf( "%s %s.\n", targetName, message); return; } // check for kill messages from the current clientNum if ( attacker == cg.snap->ps.clientNum ) { char *s; if ( cgs.gametype < GT_TEAM ) { s = va("You fragged %s\n%s place with %i", targetName, CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), cg.snap->ps.persistant[PERS_SCORE] ); } else { if(ent->generic1) s = va("You fragged your ^1TEAMMATE^7 %s", targetName ); else s = va("You fragged %s", targetName ); } #ifdef MISSIONPACK if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) { CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, (int)(BIGCHAR_WIDTH * cg_fragmsgsize.value) ); } #else CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, (int)(BIGCHAR_WIDTH * cg_fragmsgsize.value) ); #endif // print the text message as well } // check for double client messages if ( !attackerInfo ) { attacker = ENTITYNUM_WORLD; strcpy( attackerName, "noname" ); } else { Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); strcat( attackerName, S_COLOR_WHITE ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( attacker != ENTITYNUM_WORLD ) { if(ent->generic1) { message = "was killed by ^1TEAMMATE^7"; } else switch (mod) { case MOD_GRAPPLE: message = "was caught by"; break; case MOD_GAUNTLET: message = "was pummeled by"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_GRENADE: message = "ate"; message2 = "'s grenade"; break; case MOD_GRENADE_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_ROCKET_SPLASH: message = "almost dodged"; message2 = "'s rocket"; break; case MOD_PLASMA: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_PLASMA_SPLASH: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_RAILGUN: message = "was railed by"; break; case MOD_LIGHTNING: message = "was electrocuted by"; break; case MOD_BFG: case MOD_BFG_SPLASH: message = "was blasted by"; message2 = "'s BFG"; break; case MOD_NAIL: message = "was nailed by"; break; case MOD_CHAINGUN: message = "got lead poisoning from"; message2 = "'s Chaingun"; break; case MOD_PROXIMITY_MINE: message = "was too close to"; message2 = "'s Prox Mine"; break; case MOD_KAMIKAZE: message = "falls to"; message2 = "'s Kamikaze blast"; break; case MOD_JUICED: message = "was juiced by"; break; case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; case MOD_LAVA: message = "was given a hot bath by"; break; case MOD_SLIME: message = "was given a acid bath by"; break; case MOD_FALLING: message = "was given a small push by"; break; case MOD_TRIGGER_HURT: message = "was helped on the way by"; break; case MOD_CRUSH: message = "was crushed in"; message2 = "'s trap"; break; default: message = "was killed by"; break; } if (message) { CG_Printf( "%s %s %s%s\n", targetName, message, attackerName, message2); return; } } // we don't know what it was CG_Printf( "%s died.\n", targetName ); } //========================================================================== /* =============== CG_UseItem =============== */ static void CG_UseItem( centity_t *cent ) { clientInfo_t *ci; int itemNum, clientNum; gitem_t *item; entityState_t *es; es = ¢->currentState; itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0; if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) { itemNum = 0; } // print a message if the local player if ( es->number == cg.snap->ps.clientNum ) { if ( !itemNum ) { CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } else { item = BG_FindItemForHoldable( itemNum ); CG_CenterPrint( va("Use %s", item->pickup_name), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } } switch ( itemNum ) { default: case HI_NONE: trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound ); break; case HI_TELEPORTER: break; case HI_MEDKIT: clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; ci->medkitUsageTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound ); break; //#ifdef MISSIONPACK case HI_KAMIKAZE: break; case HI_PORTAL: break; case HI_INVULNERABILITY: trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useInvulnerabilitySound ); break; //#endif } } static qboolean CG_WeaponHigher(int currentWeapon, int newWeapon) { char *currentScore = NULL; char *newScore = NULL; char weapon[5]; Com_sprintf(weapon,5,"/%i/",currentWeapon); currentScore = strstr(cg_weaponOrder.string,weapon); Com_sprintf(weapon,5,"/%i/",newWeapon); newScore = strstr(cg_weaponOrder.string,weapon); if(!newScore || !currentScore) return qfalse; if(newScore>currentScore) return qtrue; else return qfalse; } /* ================ CG_ItemPickup A new item was picked up this frame ================ */ static void CG_ItemPickup( int itemNum ) { cg.itemPickup = itemNum; cg.itemPickupTime = cg.time; cg.itemPickupBlendTime = cg.time; // see if it should be the grabbed weapon if ( bg_itemlist[itemNum].giType == IT_WEAPON ) { // select it immediately /* always*/ if ( cg_autoswitch.integer == 1 && bg_itemlist[itemNum].giTag != WP_MACHINEGUN ) { cg.weaponSelectTime = cg.time; cg.weaponSelect = bg_itemlist[itemNum].giTag; } /* if new */ if ( cg_autoswitch.integer == 2 && 0 == (cg.snap->ps.stats[ STAT_WEAPONS ] & (1 << bg_itemlist[itemNum].giTag) ) ) { cg.weaponSelectTime = cg.time; cg.weaponSelect = bg_itemlist[itemNum].giTag; } /* if better */ if ( cg_autoswitch.integer == 3 && CG_WeaponHigher(cg.weaponSelect,bg_itemlist[itemNum].giTag)) { cg.weaponSelectTime = cg.time; cg.weaponSelect = bg_itemlist[itemNum].giTag; } /* if new and better */ if ( cg_autoswitch.integer == 4 && 0 == (cg.snap->ps.stats[ STAT_WEAPONS ] & (1 << bg_itemlist[itemNum].giTag) ) && CG_WeaponHigher(cg.weaponSelect,bg_itemlist[itemNum].giTag)) { cg.weaponSelectTime = cg.time; cg.weaponSelect = bg_itemlist[itemNum].giTag; } // } } /* ================ CG_WaterLevel Returns waterlevel for entity origin ================ */ int CG_WaterLevel(centity_t *cent) { vec3_t point; int contents, sample1, sample2, anim, waterlevel; // get waterlevel, accounting for ducking waterlevel = 0; VectorCopy(cent->lerpOrigin, point); point[2] += MINS_Z + 1; anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if (anim == LEGS_WALKCR || anim == LEGS_IDLECR) { point[2] += CROUCH_VIEWHEIGHT; } else { point[2] += DEFAULT_VIEWHEIGHT; } contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { sample2 = point[2] - MINS_Z; sample1 = sample2 / 2; waterlevel = 1; point[2] = cent->lerpOrigin[2] + MINS_Z + sample1; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 2; point[2] = cent->lerpOrigin[2] + MINS_Z + sample2; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 3; } } } return waterlevel; } /* ================ CG_PainEvent Also called by playerstate transition ================ */ void CG_PainEvent( centity_t *cent, int health ) { char *snd; // don't do more than two pain sounds a second if ( cg.time - cent->pe.painTime < 500 ) { return; } if ( health < 25 ) { snd = "*pain25_1.wav"; } else if ( health < 50 ) { snd = "*pain50_1.wav"; } else if ( health < 75 ) { snd = "*pain75_1.wav"; } else { snd = "*pain100_1.wav"; } // play a gurp sound instead of a normal pain sound if (CG_WaterLevel(cent) >= 1) { if (rand()&1) { trap_S_StartSound(NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound(cent->currentState.number, "sound/player/gurp1.wav")); } else { trap_S_StartSound(NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound(cent->currentState.number, "sound/player/gurp2.wav")); } } else { trap_S_StartSound(NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound(cent->currentState.number, snd)); } // save pain time for programitic twitch animation cent->pe.painTime = cg.time; cent->pe.painDirection ^= 1; } /* ============== CG_EntityEvent An entity has an event value also called by CG_CheckPlayerstateEvents ============== */ #define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");} void CG_EntityEvent( centity_t *cent, vec3_t position ) { entityState_t *es; int event; vec3_t dir; const char *s; int clientNum; clientInfo_t *ci; es = ¢->currentState; event = es->event & ~EV_EVENT_BITS; if ( cg_debugEvents.integer ) { CG_Printf( "ent:%3i event:%3i ", es->number, event ); } if ( !event ) { DEBUGNAME("ZEROEVENT"); return; } clientNum = es->clientNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; switch ( event ) { // // movement generated events // case EV_FOOTSTEP: DEBUGNAME("EV_FOOTSTEP"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ ci->footsteps ][rand()&3] ); } break; case EV_FOOTSTEP_METAL: DEBUGNAME("EV_FOOTSTEP_METAL"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] ); } break; case EV_FOOTSPLASH: DEBUGNAME("EV_FOOTSPLASH"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FOOTWADE: DEBUGNAME("EV_FOOTWADE"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_SWIM: DEBUGNAME("EV_SWIM"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FALL_SHORT: DEBUGNAME("EV_FALL_SHORT"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound ); if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -8; cg.landTime = cg.time; } break; case EV_FALL_MEDIUM: DEBUGNAME("EV_FALL_MEDIUM"); // use normal pain sound trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) ); if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -16; cg.landTime = cg.time; } break; case EV_FALL_FAR: DEBUGNAME("EV_FALL_FAR"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -24; cg.landTime = cg.time; } break; case EV_STEP_4: case EV_STEP_8: case EV_STEP_12: case EV_STEP_16: // smooth out step up transitions DEBUGNAME("EV_STEP"); { float oldStep; int delta; int step; if ( clientNum != cg.predictedPlayerState.clientNum ) { break; } // if we are interpolating, we don't need to smooth steps if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || cg_nopredict.integer || cg_synchronousClients.integer ) { break; } // check for stepping up before a previous step is completed delta = cg.time - cg.stepTime; if (delta < STEP_TIME) { oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME; } else { oldStep = 0; } // add this amount step = 4 * (event - EV_STEP_4 + 1 ); cg.stepChange = oldStep + step; if ( cg.stepChange > MAX_STEP_CHANGE ) { cg.stepChange = MAX_STEP_CHANGE; } cg.stepTime = cg.time; break; } case EV_JUMP_PAD: DEBUGNAME("EV_JUMP_PAD"); // CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm ); { localEntity_t *smoke; vec3_t up = {0, 0, 1}; smoke = CG_SmokePuff( cent->lerpOrigin, up, 32, 1, 1, 1, 0.33f, 1000, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.smokePuffShader ); } // boing sound at origin, jump sound on player trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound ); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); break; case EV_JUMP: DEBUGNAME("EV_JUMP"); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); break; case EV_TAUNT: DEBUGNAME("EV_TAUNT"); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) ); break; #ifdef MISSIONPACK case EV_TAUNT_YES: DEBUGNAME("EV_TAUNT_YES"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_YES); break; case EV_TAUNT_NO: DEBUGNAME("EV_TAUNT_NO"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_NO); break; case EV_TAUNT_FOLLOWME: DEBUGNAME("EV_TAUNT_FOLLOWME"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_FOLLOWME); break; case EV_TAUNT_GETFLAG: DEBUGNAME("EV_TAUNT_GETFLAG"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONGETFLAG); break; case EV_TAUNT_GUARDBASE: DEBUGNAME("EV_TAUNT_GUARDBASE"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONDEFENSE); break; case EV_TAUNT_PATROL: DEBUGNAME("EV_TAUNT_PATROL"); CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONPATROL); break; #endif case EV_WATER_TOUCH: DEBUGNAME("EV_WATER_TOUCH"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); break; case EV_WATER_LEAVE: DEBUGNAME("EV_WATER_LEAVE"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); break; case EV_WATER_UNDER: DEBUGNAME("EV_WATER_UNDER"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); break; case EV_WATER_CLEAR: DEBUGNAME("EV_WATER_CLEAR"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); break; case EV_ITEM_PICKUP: DEBUGNAME("EV_ITEM_PICKUP"); { gitem_t *item; int index; index = es->eventParm; // player predicted if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; // powerups and team items will have a separate global sound, this one // will be played at prediction time if ( item->giType == IT_POWERUP || item->giType == IT_TEAM) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.n_healthSound ); } else if (item->giType == IT_PERSISTANT_POWERUP) { #ifdef MISSIONPACK switch (item->giTag ) { case PW_SCOUT: trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.scoutSound ); break; case PW_GUARD: trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.guardSound ); break; case PW_DOUBLER: trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.doublerSound ); break; case PW_AMMOREGEN: trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.ammoregenSound ); break; } #endif } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; case EV_GLOBAL_ITEM_PICKUP: DEBUGNAME("EV_GLOBAL_ITEM_PICKUP"); { gitem_t *item; int index; index = es->eventParm; // player predicted if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; // powerup pickups are global if( item->pickup_sound ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; // // weapon events // case EV_NOAMMO: DEBUGNAME("EV_NOAMMO"); // trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); if ( es->number == cg.snap->ps.clientNum ) { CG_OutOfAmmoChange(); } break; case EV_CHANGE_WEAPON: DEBUGNAME("EV_CHANGE_WEAPON"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); break; case EV_FIRE_WEAPON: DEBUGNAME("EV_FIRE_WEAPON"); CG_FireWeapon( cent ); break; case EV_USE_ITEM0: DEBUGNAME("EV_USE_ITEM0"); CG_UseItem( cent ); break; case EV_USE_ITEM1: DEBUGNAME("EV_USE_ITEM1"); CG_UseItem( cent ); break; case EV_USE_ITEM2: DEBUGNAME("EV_USE_ITEM2"); CG_UseItem( cent ); break; case EV_USE_ITEM3: DEBUGNAME("EV_USE_ITEM3"); CG_UseItem( cent ); break; case EV_USE_ITEM4: DEBUGNAME("EV_USE_ITEM4"); CG_UseItem( cent ); break; case EV_USE_ITEM5: DEBUGNAME("EV_USE_ITEM5"); CG_UseItem( cent ); break; case EV_USE_ITEM6: DEBUGNAME("EV_USE_ITEM6"); CG_UseItem( cent ); break; case EV_USE_ITEM7: DEBUGNAME("EV_USE_ITEM7"); CG_UseItem( cent ); break; case EV_USE_ITEM8: DEBUGNAME("EV_USE_ITEM8"); CG_UseItem( cent ); break; case EV_USE_ITEM9: DEBUGNAME("EV_USE_ITEM9"); CG_UseItem( cent ); break; case EV_USE_ITEM10: DEBUGNAME("EV_USE_ITEM10"); CG_UseItem( cent ); break; case EV_USE_ITEM11: DEBUGNAME("EV_USE_ITEM11"); CG_UseItem( cent ); break; case EV_USE_ITEM12: DEBUGNAME("EV_USE_ITEM12"); CG_UseItem( cent ); break; case EV_USE_ITEM13: DEBUGNAME("EV_USE_ITEM13"); CG_UseItem( cent ); break; case EV_USE_ITEM14: DEBUGNAME("EV_USE_ITEM14"); CG_UseItem( cent ); break; //================================================================= // // other events // case EV_PLAYER_TELEPORT_IN: DEBUGNAME("EV_PLAYER_TELEPORT_IN"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound ); CG_SpawnEffect( position); break; case EV_PLAYER_TELEPORT_OUT: DEBUGNAME("EV_PLAYER_TELEPORT_OUT"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound ); CG_SpawnEffect( position); break; case EV_ITEM_POP: DEBUGNAME("EV_ITEM_POP"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_ITEM_RESPAWN: DEBUGNAME("EV_ITEM_RESPAWN"); cent->miscTime = cg.time; // scale up from this trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_GRENADE_BOUNCE: DEBUGNAME("EV_GRENADE_BOUNCE"); if ( rand() & 1 ) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound ); } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound ); } break; case EV_PROXIMITY_MINE_STICK: DEBUGNAME("EV_PROXIMITY_MINE_STICK"); if( es->eventParm & SURF_FLESH ) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimplSound ); } else if( es->eventParm & SURF_METALSTEPS ) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpmSound ); } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpdSound ); } break; case EV_PROXIMITY_MINE_TRIGGER: DEBUGNAME("EV_PROXIMITY_MINE_TRIGGER"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbactvSound ); break; case EV_KAMIKAZE: DEBUGNAME("EV_KAMIKAZE"); CG_KamikazeEffect( cent->lerpOrigin ); break; case EV_OBELISKEXPLODE: DEBUGNAME("EV_OBELISKEXPLODE"); CG_ObeliskExplode( cent->lerpOrigin, es->eventParm ); break; case EV_OBELISKPAIN: DEBUGNAME("EV_OBELISKPAIN"); CG_ObeliskPain( cent->lerpOrigin ); break; case EV_INVUL_IMPACT: DEBUGNAME("EV_INVUL_IMPACT"); CG_InvulnerabilityImpact( cent->lerpOrigin, cent->currentState.angles ); break; case EV_JUICED: DEBUGNAME("EV_JUICED"); CG_InvulnerabilityJuiced( cent->lerpOrigin ); break; case EV_LIGHTNINGBOLT: DEBUGNAME("EV_LIGHTNINGBOLT"); CG_LightningBoltBeam(es->origin2, es->pos.trBase); break; case EV_SCOREPLUM: DEBUGNAME("EV_SCOREPLUM"); CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time ); break; // // missile impacts // case EV_MISSILE_HIT: DEBUGNAME("EV_MISSILE_HIT"); ByteToDir( es->eventParm, dir ); CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum ); break; case EV_MISSILE_MISS: DEBUGNAME("EV_MISSILE_MISS"); ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT ); break; case EV_MISSILE_MISS_METAL: DEBUGNAME("EV_MISSILE_MISS_METAL"); ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL ); break; case EV_RAILTRAIL: DEBUGNAME("EV_RAILTRAIL"); cent->currentState.weapon = WP_RAILGUN; //unlagged - attack prediction #2 // if the client is us, unlagged is on server-side, and we've got it client-side if ( es->clientNum == cg.predictedPlayerState.clientNum && cgs.delagHitscan && (cg_delag.integer & 1 || cg_delag.integer & 16) ) { // do nothing, because it was already predicted //Com_Printf("Ignoring rail trail event\n"); } else { if(es->clientNum == cg.snap->ps.clientNum && !cg.renderingThirdPerson) { if(cg_drawGun.integer == 2) VectorMA(es->origin2, 8, cg.refdef.viewaxis[1], es->origin2); else if(cg_drawGun.integer == 3) VectorMA(es->origin2, 4, cg.refdef.viewaxis[1], es->origin2); } // draw a rail trail, because it wasn't predicted CG_RailTrail( ci, es->origin2, es->pos.trBase ); // if the end was on a nomark surface, don't make an explosion if ( es->eventParm != 255 ) { ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT ); } //Com_Printf("Non-predicted rail trail\n"); } //unlagged - attack prediction #2 break; case EV_BULLET_HIT_WALL: DEBUGNAME("EV_BULLET_HIT_WALL"); //unlagged - attack prediction #2 // if the client is us, unlagged is on server-side, and we've got it client-side if ( es->clientNum == cg.predictedPlayerState.clientNum && cgs.delagHitscan && (cg_delag.integer & 1 || cg_delag.integer & 2) ) { // do nothing, because it was already predicted //Com_Printf("Ignoring bullet event\n"); } else { // do the bullet, because it wasn't predicted ByteToDir( es->eventParm, dir ); CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD ); //Com_Printf("Non-predicted bullet\n"); } //unlagged - attack prediction #2 break; case EV_BULLET_HIT_FLESH: DEBUGNAME("EV_BULLET_HIT_FLESH"); //unlagged - attack prediction #2 // if the client is us, unlagged is on server-side, and we've got it client-side if ( es->clientNum == cg.predictedPlayerState.clientNum && cgs.delagHitscan && (cg_delag.integer & 1 || cg_delag.integer & 2) ) { // do nothing, because it was already predicted //Com_Printf("Ignoring bullet event\n"); } else { // do the bullet, because it wasn't predicted CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm ); //Com_Printf("Non-predicted bullet\n"); } //unlagged - attack prediction #2 break; case EV_SHOTGUN: DEBUGNAME("EV_SHOTGUN"); //unlagged - attack prediction #2 // if the client is us, unlagged is on server-side, and we've got it client-side if ( es->otherEntityNum == cg.predictedPlayerState.clientNum && cgs.delagHitscan && (cg_delag.integer & 1 || cg_delag.integer & 4) ) { // do nothing, because it was already predicted //Com_Printf("Ignoring shotgun event\n"); } else { // do the shotgun pattern, because it wasn't predicted CG_ShotgunFire( es ); //Com_Printf("Non-predicted shotgun pattern\n"); } //unlagged - attack prediction #2 break; case EV_GENERAL_SOUND: DEBUGNAME("EV_GENERAL_SOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) ); } break; case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes DEBUGNAME("EV_GLOBAL_SOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); } break; case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes { DEBUGNAME("EV_GLOBAL_TEAM_SOUND"); switch( es->eventParm ) { case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) CG_AddBufferedSound( cgs.media.captureYourTeamSound ); else CG_AddBufferedSound( cgs.media.captureOpponentSound ); break; case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) CG_AddBufferedSound( cgs.media.captureYourTeamSound ); else CG_AddBufferedSound( cgs.media.captureOpponentSound ); break; case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) CG_AddBufferedSound( cgs.media.returnYourTeamSound ); else CG_AddBufferedSound( cgs.media.returnOpponentSound ); // CG_AddBufferedSound( cgs.media.blueFlagReturnedSound ); break; case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) CG_AddBufferedSound( cgs.media.returnYourTeamSound ); else CG_AddBufferedSound( cgs.media.returnOpponentSound ); // CG_AddBufferedSound( cgs.media.redFlagReturnedSound ); break; case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { } else { if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { //#ifdef MISSIONPACK if (cgs.gametype == GT_1FCTF) CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); else //#endif CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); } else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { //#ifdef MISSIONPACK if (cgs.gametype == GT_1FCTF) CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); else //#endif CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); } } break; case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { } else { if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { //#ifdef MISSIONPACK if (cgs.gametype == GT_1FCTF) CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); else //#endif CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); } else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { //#ifdef MISSIONPACK if (cgs.gametype == GT_1FCTF) CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); else //#endif CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); } } break; case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); } break; case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); } break; case GTS_REDTEAM_SCORED: CG_AddBufferedSound(cgs.media.redScoredSound); break; case GTS_BLUETEAM_SCORED: CG_AddBufferedSound(cgs.media.blueScoredSound); break; case GTS_REDTEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.redLeadsSound); break; case GTS_BLUETEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.blueLeadsSound); break; case GTS_TEAMS_ARE_TIED: CG_AddBufferedSound( cgs.media.teamsTiedSound ); break; case GTS_KAMIKAZE: trap_S_StartLocalSound(cgs.media.kamikazeFarSound, CHAN_ANNOUNCER); break; default: break; } break; } case EV_PAIN: // local player sounds are triggered in CG_CheckLocalSounds, // so ignore events on the player DEBUGNAME("EV_PAIN"); if ( cent->currentState.number != cg.snap->ps.clientNum ) { CG_PainEvent( cent, es->eventParm ); } break; case EV_DEATH1: case EV_DEATH2: case EV_DEATH3: DEBUGNAME("EV_DEATHx"); if (CG_WaterLevel(cent) >= 1) { trap_S_StartSound(NULL, es->number, CHAN_VOICE, CG_CustomSound(es->number, "*drown.wav")); } else { trap_S_StartSound(NULL, es->number, CHAN_VOICE, CG_CustomSound(es->number, va("*death%i.wav", event - EV_DEATH1 + 1))); } break; case EV_OBITUARY: DEBUGNAME("EV_OBITUARY"); CG_Obituary( es ); break; // // powerup events // case EV_POWERUP_QUAD: DEBUGNAME("EV_POWERUP_QUAD"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_QUAD; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound ); break; case EV_POWERUP_BATTLESUIT: DEBUGNAME("EV_POWERUP_BATTLESUIT"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_BATTLESUIT; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound ); break; case EV_POWERUP_REGEN: DEBUGNAME("EV_POWERUP_REGEN"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_REGEN; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound ); break; case EV_GIB_PLAYER: DEBUGNAME("EV_GIB_PLAYER"); // don't play gib sound when using the kamikaze because it interferes // with the kamikaze sound, downside is that the gib sound will also // not be played when someone is gibbed while just carrying the kamikaze if ( !(es->eFlags & EF_KAMIKAZE) ) { trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound ); } CG_GibPlayer( cent->lerpOrigin ); break; case EV_STOPLOOPINGSOUND: DEBUGNAME("EV_STOPLOOPINGSOUND"); trap_S_StopLoopingSound( es->number ); es->loopSound = 0; break; case EV_DEBUG_LINE: DEBUGNAME("EV_DEBUG_LINE"); CG_Beam( cent ); break; default: DEBUGNAME("UNKNOWN"); CG_Error( "Unknown event: %i", event ); break; } } /* ============== CG_CheckEvents ============== */ void CG_CheckEvents( centity_t *cent ) { // check for event-only entities if ( cent->currentState.eType > ET_EVENTS ) { if ( cent->previousEvent ) { return; // already fired } // if this is a player event set the entity number of the client entity number if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { cent->currentState.number = cent->currentState.otherEntityNum; } cent->previousEvent = 1; cent->currentState.event = cent->currentState.eType - ET_EVENTS; } else { // check for events riding with another entity if ( cent->currentState.event == cent->previousEvent ) { return; } cent->previousEvent = cent->currentState.event; if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { return; } } // calculate the position at exactly the frame time BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); CG_SetEntitySoundPosition( cent ); CG_EntityEvent( cent, cent->lerpOrigin ); } openarena_0.8.8.orig/code/cgame/cg_snapshot.c0000644000175000017500000002571211656310265017700 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_snapshot.c -- things that happen on snapshot transition, // not necessarily every single rendered frame #include "cg_local.h" /* ================== CG_ResetEntity ================== */ static void CG_ResetEntity( centity_t *cent ) { // if the previous snapshot this entity was updated in is at least // an event window back in time then we can reset the previous event if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) { cent->previousEvent = 0; } cent->trailTime = cg.snap->serverTime; VectorCopy (cent->currentState.origin, cent->lerpOrigin); VectorCopy (cent->currentState.angles, cent->lerpAngles); if ( cent->currentState.eType == ET_PLAYER ) { CG_ResetPlayerEntity( cent ); } } /* =============== CG_TransitionEntity cent->nextState is moved to cent->currentState and events are fired =============== */ //unlagged - early transitioning // used to be static, now needed to transition entities from within cg_ents.c void CG_TransitionEntity( centity_t *cent ) { cent->currentState = cent->nextState; cent->currentValid = qtrue; // reset if the entity wasn't in the last frame or was teleported if ( !cent->interpolate ) { CG_ResetEntity( cent ); } // clear the next state. if will be set by the next CG_SetNextSnap cent->interpolate = qfalse; // check for events CG_CheckEvents( cent ); } /* ================== CG_SetInitialSnapshot This will only happen on the very first snapshot, or on tourney restarts. All other times will use CG_TransitionSnapshot instead. FIXME: Also called by map_restart? ================== */ void CG_SetInitialSnapshot( snapshot_t *snap ) { int i; centity_t *cent; entityState_t *state; cg.snap = snap; BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse ); // sort out solid entities CG_BuildSolidList(); CG_ExecuteNewServerCommands( snap->serverCommandSequence ); // set our local weapon selection pointer to // what the server has indicated the current weapon is CG_Respawn(); for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { state = &cg.snap->entities[ i ]; cent = &cg_entities[ state->number ]; memcpy(¢->currentState, state, sizeof(entityState_t)); //cent->currentState = *state; cent->interpolate = qfalse; cent->currentValid = qtrue; CG_ResetEntity( cent ); // check for events CG_CheckEvents( cent ); } } /* =================== CG_TransitionSnapshot The transition point from snap to nextSnap has passed =================== */ static void CG_TransitionSnapshot( void ) { centity_t *cent; snapshot_t *oldFrame; int i; if ( !cg.snap ) { CG_Error( "CG_TransitionSnapshot: NULL cg.snap" ); } if ( !cg.nextSnap ) { CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" ); } // execute any server string commands before transitioning entities CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); // if we had a map_restart, set everthing with initial if ( !cg.snap ) { } // clear the currentValid flag for all entities in the existing snapshot for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { cent = &cg_entities[ cg.snap->entities[ i ].number ]; cent->currentValid = qfalse; } // move nextSnap to snap and do the transitions oldFrame = cg.snap; cg.snap = cg.nextSnap; BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse ); cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse; for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { cent = &cg_entities[ cg.snap->entities[ i ].number ]; CG_TransitionEntity( cent ); // remember time of snapshot this entity was last updated in cent->snapShotTime = cg.snap->serverTime; } cg.nextSnap = NULL; // check for playerstate transition events if ( oldFrame ) { playerState_t *ops, *ps; ops = &oldFrame->ps; ps = &cg.snap->ps; // teleporting checks are irrespective of prediction if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) { cg.thisFrameTeleport = qtrue; // will be cleared by prediction code } // if we are not doing client side movement prediction for any // reason, then the client events and view changes will be issued now if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || cg_nopredict.integer || cg_synchronousClients.integer ) { CG_TransitionPlayerState( ps, ops ); } } } /* =================== CG_SetNextSnap A new snapshot has just been read in from the client system. =================== */ static void CG_SetNextSnap( snapshot_t *snap ) { int num; entityState_t *es; centity_t *cent; cg.nextSnap = snap; BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse ); cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue; // check for extrapolation errors for ( num = 0 ; num < snap->numEntities ; num++ ) { es = &snap->entities[num]; cent = &cg_entities[ es->number ]; memcpy(¢->nextState, es, sizeof(entityState_t)); //cent->nextState = *es; // if this frame is a teleport, or the entity wasn't in the // previous frame, don't interpolate if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) { cent->interpolate = qfalse; } else { cent->interpolate = qtrue; } } // if the next frame is a teleport for the playerstate, we // can't interpolate during demos if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) { cg.nextFrameTeleport = qtrue; } else { cg.nextFrameTeleport = qfalse; } // if changing follow mode, don't interpolate if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) { cg.nextFrameTeleport = qtrue; } // if changing server restarts, don't interpolate if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) { cg.nextFrameTeleport = qtrue; } // sort out solid entities CG_BuildSolidList(); } /* ======================== CG_ReadNextSnapshot This is the only place new snapshots are requested This may increment cgs.processedSnapshotNum multiple times if the client system fails to return a valid snapshot. ======================== */ static snapshot_t *CG_ReadNextSnapshot( void ) { qboolean r; snapshot_t *dest; if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) { CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i", cg.latestSnapshotNum, cgs.processedSnapshotNum ); } while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) { // decide which of the two slots to load it into if ( cg.snap == &cg.activeSnapshots[0] ) { dest = &cg.activeSnapshots[1]; } else { dest = &cg.activeSnapshots[0]; } // try to read the snapshot from the client system cgs.processedSnapshotNum++; r = trap_GetSnapshot( cgs.processedSnapshotNum, dest ); // FIXME: why would trap_GetSnapshot return a snapshot with the same server time if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) { //continue; } // if it succeeded, return if ( r ) { CG_AddLagometerSnapshotInfo( dest ); return dest; } // a GetSnapshot will return failure if the snapshot // never arrived, or is so old that its entities // have been shoved off the end of the circular // buffer in the client system. // record as a dropped packet CG_AddLagometerSnapshotInfo( NULL ); // If there are additional snapshots, continue trying to // read them. } // nothing left to read return NULL; } /* ============ CG_ProcessSnapshots We are trying to set up a renderable view, so determine what the simulated time is, and try to get snapshots both before and after that time if available. If we don't have a valid cg.snap after exiting this function, then a 3D game view cannot be rendered. This should only happen right after the initial connection. After cg.snap has been valid once, it will never turn invalid. Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot hasn't arrived yet (it becomes an extrapolating situation instead of an interpolating one) ============ */ void CG_ProcessSnapshots( void ) { snapshot_t *snap; int n; // see what the latest snapshot the client system has is trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime ); if ( n != cg.latestSnapshotNum ) { if ( n < cg.latestSnapshotNum ) { // this should never happen CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" ); } cg.latestSnapshotNum = n; } // If we have yet to receive a snapshot, check for it. // Once we have gotten the first snapshot, cg.snap will // always have valid data for the rest of the game while ( !cg.snap ) { snap = CG_ReadNextSnapshot(); if ( !snap ) { // we can't continue until we get a snapshot return; } // set our weapon selection to what // the playerstate is currently using if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { CG_SetInitialSnapshot( snap ); } } // loop until we either have a valid nextSnap with a serverTime // greater than cg.time to interpolate towards, or we run // out of available snapshots do { // if we don't have a nextframe, try and read a new one in if ( !cg.nextSnap ) { snap = CG_ReadNextSnapshot(); // if we still don't have a nextframe, we will just have to // extrapolate if ( !snap ) { break; } CG_SetNextSnap( snap ); // if time went backwards, we have a level restart if ( cg.nextSnap->serverTime < cg.snap->serverTime ) { CG_Error( "CG_ProcessSnapshots: Server time went backwards" ); } } // if our time is < nextFrame's, we have a nice interpolating state if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) { break; } // we have passed the transition from nextFrame to frame CG_TransitionSnapshot(); } while ( 1 ); // assert our valid conditions upon exiting if ( cg.snap == NULL ) { CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" ); } if ( cg.time < cg.snap->serverTime ) { // this can happen right after a vid_restart cg.time = cg.snap->serverTime; } if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) { CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" ); } } openarena_0.8.8.orig/code/cgame/cg_particles.c0000644000175000017500000011752411656310265020032 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // Rafael particles // cg_particles.c #include "cg_local.h" #define BLOODRED 2 #define EMISIVEFADE 3 #define GREY75 4 typedef struct particle_s { struct particle_s *next; float time; float endtime; vec3_t org; vec3_t vel; vec3_t accel; int color; float colorvel; float alpha; float alphavel; int type; qhandle_t pshader; float height; float width; float endheight; float endwidth; float start; float end; float startfade; qboolean rotate; int snum; qboolean link; // Ridah int shaderAnim; int roll; int accumroll; } cparticle_t; typedef enum { P_NONE, P_WEATHER, P_FLAT, P_SMOKE, P_ROTATE, P_WEATHER_TURBULENT, P_ANIM, // Ridah P_BAT, P_BLEED, P_FLAT_SCALEUP, P_FLAT_SCALEUP_FADE, P_WEATHER_FLURRY, P_SMOKE_IMPACT, P_BUBBLE, P_BUBBLE_TURBULENT, P_SPRITE } particle_type_t; #define MAX_SHADER_ANIMS 32 #define MAX_SHADER_ANIM_FRAMES 64 static char *shaderAnimNames[MAX_SHADER_ANIMS] = { "explode1", "blacksmokeanim", "twiltb2", "expblue", "blacksmokeanimb", // uses 'explode1' sequence "blood", NULL }; static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; static int shaderAnimCounts[MAX_SHADER_ANIMS] = { 23, 25, 45, 25, 23, 5, }; static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { 1.405f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; static int numShaderAnims; // done. #define PARTICLE_GRAVITY 40 #define MAX_PARTICLES 1024 * 8 cparticle_t *active_particles, *free_particles; cparticle_t particles[MAX_PARTICLES]; int cl_numparticles = MAX_PARTICLES; qboolean initparticles = qfalse; vec3_t vforward, vright, vup; vec3_t rforward, rright, rup; float oldtime; /* =============== CL_ClearParticles =============== */ void CG_ClearParticles (void) { int i; memset( particles, 0, sizeof(particles) ); free_particles = &particles[0]; active_particles = NULL; for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) invratio = 1; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) alpha = 1; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (vup, ru); VectorCopy (vright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); } // Ridah, made this static so it doesn't interfere with other files static float roll = 0.0; /* =============== CG_AddParticles =============== */ void CG_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int type; vec3_t rotate_ang; if (!initparticles) CG_ClearParticles (); VectorCopy( cg.refdef.viewaxis[0], vforward ); VectorCopy( cg.refdef.viewaxis[1], vright ); VectorCopy( cg.refdef.viewaxis[2], vup ); vectoangles( cg.refdef.viewaxis[0], rotate_ang ); roll += ((cg.time - oldtime) * 0.1) ; rotate_ang[ROLL] += (roll*0.9); AngleVectors ( rotate_ang, rforward, rright, rup); oldtime = cg.time; active = NULL; tail = NULL; for (p=active_particles ; p ; p=next) { next = p->next; time = (cg.time - p->time)*0.001; alpha = p->alpha + time*p->alphavel; if (alpha <= 0) { // faded out p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_WEATHER_FLURRY) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_FLAT_SCALEUP_FADE) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { // temporary sprite CG_AddParticleToScene (p, p->org, alpha); p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } p->next = NULL; if (!tail) active = tail = p; else { tail->next = p; tail = p; } if (alpha > 1.0) alpha = 1; color = p->color; time2 = time*time; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; type = p->type; CG_AddParticleToScene (p, org, alpha); } active_particles = active; } /* ====================== CG_AddParticles ====================== */ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) { cparticle_t *p; qboolean turb = qtrue; if (!pshader) CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.90f; p->alphavel = 0; p->start = cent->currentState.origin2[0]; p->end = cent->currentState.origin2[1]; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->pshader = pshader; if (rand()%100 > 90) { p->height = 32; p->width = 32; p->alpha = 0.10f; } else { p->height = 1; p->width = 1; } p->vel[2] = -20; p->type = P_WEATHER_FLURRY; if (turb) p->vel[2] = -10; VectorCopy(cent->currentState.origin, p->org); p->org[0] = p->org[0]; p->org[1] = p->org[1]; p->org[2] = p->org[2]; p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); p->vel[2] += cent->currentState.angles[2]; if (turb) { p->accel[0] = crandom () * 16; p->accel[1] = crandom () * 16; } } void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; p->height = 1; p->width = 1; p->vel[2] = -50; if (turb) { p->type = P_WEATHER_TURBULENT; p->vel[2] = -50 * 1.3; } else { p->type = P_WEATHER; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; float randsize; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; randsize = 1 + (crandom() * 0.5); p->height = randsize; p->width = randsize; p->vel[2] = 50 + ( crandom() * 10 ); if (turb) { p->type = P_BUBBLE_TURBULENT; p->vel[2] = 50 * 1.3; } else { p->type = P_BUBBLE; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) { // using cent->density = enttime // cent->frame = startfade cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSmoke == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->color = 0; p->alpha = 1.0; p->alphavel = 0; p->start = cent->currentState.origin[2]; p->end = cent->currentState.origin2[2]; p->pshader = pshader; p->rotate = qfalse; p->height = 8; p->width = 8; p->endheight = 32; p->endwidth = 32; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[2] = 5; if (cent->currentState.frame == 1)// reverse gravity p->vel[2] *= -1; p->roll = 8 + (crandom() * 4); } void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 1.0; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->accel[2] = -60; p->vel[2] += -20; } /* ====================== CG_ParticleExplosion ====================== */ void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) { cparticle_t *p; int anim; if (animStr < (char *)10) CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); // find the animation string for (anim=0; shaderAnimNames[anim]; anim++) { if (!Q_stricmp( animStr, shaderAnimNames[anim] )) break; } if (!shaderAnimNames[anim]) { CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); return; } if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; if (duration < 0) { duration *= -1; p->roll = 0; } else { p->roll = crandom()*179; } p->shaderAnim = anim; p->width = sizeStart; p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction p->endheight = sizeEnd; p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; p->endtime = cg.time + duration; p->type = P_ANIM; VectorCopy( origin, p->org ); VectorCopy( vel, p->vel ); VectorClear( p->accel ); } // Rafael Shrapnel void CG_AddParticleShrapnel (localEntity_t *le) { return; } // done. int CG_NewParticleArea (int num) { // const char *str; char *str; char *token; int type; vec3_t origin, origin2; int i; float range = 0; int turb; int numparticles; int snum; str = (char *) CG_ConfigString (num); if (!str[0]) return (0); // returns type 128 64 or 32 token = COM_Parse (&str); type = atoi (token); if (type == 1) range = 128; else if (type == 2) range = 64; else if (type == 3) range = 32; else if (type == 0) range = 256; else if (type == 4) range = 8; else if (type == 5) range = 16; else if (type == 6) range = 32; else if (type == 7) range = 64; for (i=0; i<3; i++) { token = COM_Parse (&str); origin[i] = atof (token); } for (i=0; i<3; i++) { token = COM_Parse (&str); origin2[i] = atof (token); } token = COM_Parse (&str); numparticles = atoi (token); token = COM_Parse (&str); turb = atoi (token); token = COM_Parse (&str); snum = atoi (token); for (i=0; i= 4) CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); else CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); } return (1); } void CG_SnowLink (centity_t *cent, qboolean particleOn) { cparticle_t *p, *next; int id; id = cent->currentState.frame; for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) { if (p->snum == id) { if (particleOn) p->link = qtrue; else p->link = qfalse; } } } } void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.25; p->alphavel = 0; p->roll = crandom()*179; p->pshader = pshader; p->endtime = cg.time + 1000; p->startfade = cg.time + 100; p->width = rand()%4 + 8; p->height = rand()%4 + 8; p->endheight = p->height *2; p->endwidth = p->width * 2; p->endtime = cg.time + 500; p->type = P_SMOKE_IMPACT; VectorCopy( origin, p->org ); VectorSet(p->vel, 0, 0, 20); VectorSet(p->accel, 0, 0, 20); p->rotate = qtrue; } void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; if (fleshEntityNum) p->startfade = cg.time; else p->startfade = cg.time + 100; p->width = 4; p->height = 4; p->endheight = 4+rand()%3; p->endwidth = p->endheight; p->type = P_SMOKE; VectorCopy( start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -20; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) { cparticle_t *p; int time; int time2; float ratio; float duration = 1500; time = cg.time; time2 = cg.time + cent->currentState.time; ratio =(float)1 - ((float)time / (float)time2); if (!pshader) CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; p->startfade = p->endtime; p->width = 1; p->height = 3; p->endheight = 3; p->endwidth = 1; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org ); p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); p->vel[2] = (cent->currentState.origin2[2]); p->snum = 1.0f; VectorClear( p->accel ); p->accel[2] = -20; p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; if (cent->currentState.angles2[2]) p->endtime = cg.time + cent->currentState.angles2[2]; else p->endtime = cg.time + 60000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) { p->width = cent->currentState.angles2[0]; p->height = cent->currentState.angles2[0]; p->endheight = cent->currentState.angles2[1]; p->endwidth = cent->currentState.angles2[1]; } else { p->width = 8; p->height = 8; p->endheight = 16; p->endwidth = 16; } p->type = P_FLAT_SCALEUP; p->snum = 1.0; VectorCopy(cent->currentState.origin, p->org ); p->org[2]+= 0.55 + (crandom() * 0.5); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_OilSlickRemove (centity_t *cent) { cparticle_t *p, *next; int id; id = 1.0f; if (!id) CG_Printf ("CG_OilSlickRevove NULL id\n"); for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_FLAT_SCALEUP) { if (p->snum == id) { p->endtime = cg.time + 100; p->startfade = p->endtime; p->type = P_FLAT_SCALEUP_FADE; } } } } qboolean ValidBloodPool (vec3_t start) { #define EXTRUDE_DIST 0.5 vec3_t angles; vec3_t right, up; vec3_t this_pos, x_pos, center_pos, end_pos; float x, y; float fwidth, fheight; trace_t trace; vec3_t normal; fwidth = 16; fheight = 16; VectorSet (normal, 0, 0, 1); vectoangles (normal, angles); AngleVectors (angles, NULL, right, up); VectorMA (start, EXTRUDE_DIST, normal, center_pos); for (x= -fwidth/2; xendpos, start); legit = ValidBloodPool (start); if (!legit) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + 3000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; rndSize = 0.4 + random()*0.6; p->width = 8*rndSize; p->height = 8*rndSize; p->endheight = 16*rndSize; p->endwidth = 16*rndSize; p->type = P_FLAT_SCALEUP; VectorCopy(start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; p->color = BLOODRED; } #define NORMALSIZE 16 #define LARGESIZE 32 void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } } void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.4f; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 4); p->vel[1] += (crandom() * 4); p->vel[2] += (20 + (crandom() * 10)) * speed; p->accel[0] = crandom () * 4; p->accel[1] = crandom () * 4; } void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } } void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = rand()%179; p->pshader = pshader; if (duration > 0) p->endtime = cg.time + duration; else p->endtime = duration; p->startfade = cg.time; p->width = size; p->height = size; p->endheight = size; p->endwidth = size; p->type = P_SPRITE; VectorCopy( origin, p->org ); p->rotate = qfalse; } openarena_0.8.8.orig/code/cgame/cg_syscalls.asm0000644000175000017500000000606511656310265020234 0ustar smcvsmcvcode equ trap_Print -1 equ trap_Error -2 equ trap_Milliseconds -3 equ trap_Cvar_Register -4 equ trap_Cvar_Update -5 equ trap_Cvar_Set -6 equ trap_Cvar_VariableStringBuffer -7 equ trap_Argc -8 equ trap_Argv -9 equ trap_Args -10 equ trap_FS_FOpenFile -11 equ trap_FS_Read -12 equ trap_FS_Write -13 equ trap_FS_FCloseFile -14 equ trap_SendConsoleCommand -15 equ trap_AddCommand -16 equ trap_SendClientCommand -17 equ trap_UpdateScreen -18 equ trap_CM_LoadMap -19 equ trap_CM_NumInlineModels -20 equ trap_CM_InlineModel -21 equ trap_CM_LoadModel -22 equ trap_CM_TempBoxModel -23 equ trap_CM_PointContents -24 equ trap_CM_TransformedPointContents -25 equ trap_CM_BoxTrace -26 equ trap_CM_TransformedBoxTrace -27 equ trap_CM_MarkFragments -28 equ trap_S_StartSound -29 equ trap_S_StartLocalSound -30 equ trap_S_ClearLoopingSounds -31 equ trap_S_AddLoopingSound -32 equ trap_S_UpdateEntityPosition -33 equ trap_S_Respatialize -34 equ trap_S_RegisterSound -35 equ trap_S_StartBackgroundTrack -36 equ trap_R_LoadWorldMap -37 equ trap_R_RegisterModel -38 equ trap_R_RegisterSkin -39 equ trap_R_RegisterShader -40 equ trap_R_ClearScene -41 equ trap_R_AddRefEntityToScene -42 equ trap_R_AddPolyToScene -43 equ trap_R_AddLightToScene -44 equ trap_R_RenderScene -45 equ trap_R_SetColor -46 equ trap_R_DrawStretchPic -47 equ trap_R_ModelBounds -48 equ trap_R_LerpTag -49 equ trap_GetGlconfig -50 equ trap_GetGameState -51 equ trap_GetCurrentSnapshotNumber -52 equ trap_GetSnapshot -53 equ trap_GetServerCommand -54 equ trap_GetCurrentCmdNumber -55 equ trap_GetUserCmd -56 equ trap_SetUserCmdValue -57 equ trap_R_RegisterShaderNoMip -58 equ trap_MemoryRemaining -59 equ trap_R_RegisterFont -60 equ trap_Key_IsDown -61 equ trap_Key_GetCatcher -62 equ trap_Key_SetCatcher -63 equ trap_Key_GetKey -64 equ trap_PC_AddGlobalDefine -65 equ trap_PC_LoadSource -66 equ trap_PC_FreeSource -67 equ trap_PC_ReadToken -68 equ trap_PC_SourceFileAndLine -69 equ trap_S_StopBackgroundTrack -70 equ trap_RealTime -71 equ trap_SnapVector -72 equ trap_RemoveCommand -73 equ trap_R_LightForPoint -74 equ trap_CIN_PlayCinematic -75 equ trap_CIN_StopCinematic -76 equ trap_CIN_RunCinematic -77 equ trap_CIN_DrawCinematic -78 equ trap_CIN_SetExtents -79 equ trap_R_RemapShader -80 equ trap_S_AddRealLoopingSound -81 equ trap_S_StopLoopingSound -82 equ trap_CM_TempCapsuleModel -83 equ trap_CM_CapsuleTrace -84 equ trap_CM_TransformedCapsuleTrace -85 equ trap_R_AddAdditiveLightToScene -86 equ trap_GetEntityToken -87 equ trap_R_AddPolysToScene -88 equ trap_R_inPVS -89 equ trap_FS_Seek -90 equ memset -101 equ memcpy -102 equ strncpy -103 equ sin -104 equ cos -105 equ atan2 -106 equ sqrt -107 equ floor -108 equ ceil -109 equ testPrintInt -110 equ testPrintFloat -111 equ acos -112 openarena_0.8.8.orig/code/cgame/cg_local.h0000644000175000017500000015503011656310265017135 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "../qcommon/q_shared.h" #include "../renderer/tr_types.h" #include "../game/bg_public.h" #include "cg_public.h" #include "../game/challenges.h" // The entire cgame module is unloaded and reloaded on each level change, // so there is NO persistant data between levels on the client side. // If you absolutely need something stored, it can either be kept // by the server in the server stored userinfos, or stashed in a cvar. #ifdef MISSIONPACK #define CG_FONT_THRESHOLD 0.1 #endif #define POWERUP_BLINKS 5 #define POWERUP_BLINK_TIME 1000 #define FADE_TIME 200 #define PULSE_TIME 200 #define DAMAGE_DEFLECT_TIME 100 #define DAMAGE_RETURN_TIME 400 #define DAMAGE_TIME 500 #define LAND_DEFLECT_TIME 150 #define LAND_RETURN_TIME 300 #define STEP_TIME 200 #define DUCK_TIME 100 #define PAIN_TWITCH_TIME 200 #define WEAPON_SELECT_TIME 1400 #define ITEM_SCALEUP_TIME 1000 #define ZOOM_TIME 150 #define ITEM_BLOB_TIME 200 #define MUZZLE_FLASH_TIME 20 #define SINK_TIME 1000 // time for fragments to sink into ground before going away #define ATTACKER_HEAD_TIME 10000 #define REWARD_TIME 3000 #define PULSE_SCALE 1.5 // amount to scale up the icons when activating #define MAX_STEP_CHANGE 32 #define MAX_VERTS_ON_POLY 10 #define MAX_MARK_POLYS 256 #define STAT_MINUS 10 // num frame for '-' stats digit #define ICON_SIZE 48 #define CHAR_WIDTH 32 #define CHAR_HEIGHT 48 #define TEXT_ICON_SPACE 4 #define TEAMCHAT_WIDTH 80 #define TEAMCHAT_HEIGHT 8 // very large characters.pk #define GIANT_WIDTH 32 #define GIANT_HEIGHT 48 #define NUM_CROSSHAIRS 99 #define TEAM_OVERLAY_MAXNAME_WIDTH 12 #define TEAM_OVERLAY_MAXLOCATION_WIDTH 16 #define DEFAULT_MODEL "sarge" #ifdef MISSIONPACK #define DEFAULT_TEAM_MODEL "sergei" #define DEFAULT_TEAM_HEAD "*sergei" #else #define DEFAULT_TEAM_MODEL "sarge" #define DEFAULT_TEAM_HEAD "sarge" #endif #define DEFAULT_REDTEAM_NAME "Vim supporters" #define DEFAULT_BLUETEAM_NAME "Emacs supporters" typedef enum { FOOTSTEP_NORMAL, FOOTSTEP_BOOT, FOOTSTEP_FLESH, FOOTSTEP_MECH, FOOTSTEP_ENERGY, FOOTSTEP_METAL, FOOTSTEP_SPLASH, FOOTSTEP_TOTAL } footstep_t; typedef enum { IMPACTSOUND_DEFAULT, IMPACTSOUND_METAL, IMPACTSOUND_FLESH } impactSound_t; //================================================= // player entities need to track more information // than any other type of entity. // note that not every player entity is a client entity, // because corpses after respawn are outside the normal // client numbering range // when changing animation, set animationTime to frameTime + lerping time // The current lerp will finish out, then it will lerp to the new animation typedef struct { int oldFrame; int oldFrameTime; // time when ->oldFrame was exactly on int frame; int frameTime; // time when ->frame will be exactly on float backlerp; float yawAngle; qboolean yawing; float pitchAngle; qboolean pitching; int animationNumber; // may include ANIM_TOGGLEBIT animation_t *animation; int animationTime; // time when the first frame of the animation will be exact } lerpFrame_t; typedef struct { lerpFrame_t legs, torso, flag; int painTime; int painDirection; // flip from 0 to 1 int lightningFiring; // railgun trail spawning vec3_t railgunImpact; qboolean railgunFlash; // machinegun spinning float barrelAngle; int barrelTime; qboolean barrelSpinning; } playerEntity_t; //================================================= // centity_t have a direct corespondence with gentity_t in the game, but // only the entityState_t is directly communicated to the cgame typedef struct centity_s { entityState_t currentState; // from cg.frame entityState_t nextState; // from cg.nextFrame, if available qboolean interpolate; // true if next is valid to interpolate to qboolean currentValid; // true if cg.frame holds this entity int muzzleFlashTime; // move to playerEntity? int previousEvent; int teleportFlag; int trailTime; // so missile trails can handle dropped initial packets int dustTrailTime; int miscTime; int snapShotTime; // last time this entity was found in a snapshot playerEntity_t pe; int errorTime; // decay the error from this time vec3_t errorOrigin; vec3_t errorAngles; qboolean extrapolated; // false if origin / angles is an interpolation vec3_t rawOrigin; vec3_t rawAngles; vec3_t beamEnd; // exact interpolated position of entity on this frame vec3_t lerpOrigin; vec3_t lerpAngles; } centity_t; //====================================================================== // local entities are created as a result of events or predicted actions, // and live independantly from all server transmitted entities typedef struct markPoly_s { struct markPoly_s *prevMark, *nextMark; int time; qhandle_t markShader; qboolean alphaFade; // fade alpha instead of rgb float color[4]; poly_t poly; polyVert_t verts[MAX_VERTS_ON_POLY]; } markPoly_t; typedef enum { LE_MARK, LE_EXPLOSION, LE_SPRITE_EXPLOSION, LE_FRAGMENT, LE_MOVE_SCALE_FADE, LE_FALL_SCALE_FADE, LE_FADE_RGB, LE_SCALE_FADE, LE_SCOREPLUM, LE_KAMIKAZE, LE_INVULIMPACT, LE_INVULJUICED, LE_SHOWREFENTITY, LE_GORE } leType_t; typedef enum { LEF_PUFF_DONT_SCALE = 0x0001, // do not scale size over time LEF_TUMBLE = 0x0002, // tumble over time, used for ejecting shells LEF_SOUND1 = 0x0004, // sound 1 for kamikaze LEF_SOUND2 = 0x0008 // sound 2 for kamikaze } leFlag_t; typedef enum { LEMT_NONE, LEMT_BURN, LEMT_BLOOD } leMarkType_t; // fragment local entities can leave marks on walls typedef enum { LEBS_NONE, LEBS_BLOOD, LEBS_BRASS, LEBS_SHELL } leBounceSoundType_t; // fragment local entities can make sounds on impacts typedef struct localEntity_s { struct localEntity_s *prev, *next; leType_t leType; int leFlags; int startTime; int endTime; int fadeInTime; float lifeRate; // 1.0 / (endTime - startTime) trajectory_t pos; trajectory_t angles; float bounceFactor; // 0.0 = no bounce, 1.0 = perfect float color[4]; float radius; float light; vec3_t lightColor; leMarkType_t leMarkType; // mark to leave on fragment impact leBounceSoundType_t leBounceSoundType; refEntity_t refEntity; } localEntity_t; //====================================================================== typedef struct { int client; int score; int ping; int time; int scoreFlags; int powerUps; int accuracy; int impressiveCount; int excellentCount; int guantletCount; int defendCount; int assistCount; int captures; qboolean perfect; int team; int isDead; } score_t; // each client has an associated clientInfo_t // that contains media references necessary to present the // client model and other color coded effects // this is regenerated each time a client's configstring changes, // usually as a result of a userinfo (name, model, etc) change #define MAX_CUSTOM_SOUNDS 32 typedef struct { qboolean infoValid; char name[MAX_QPATH]; team_t team; int botSkill; // 0 = not bot, 1-5 = bot vec3_t color1; vec3_t color2; int score; // updated by score servercmds int location; // location index for team mode int health; // you only get this info about your teammates int armor; int curWeapon; int handicap; int wins, losses; // in tourney mode int teamTask; // task in teamplay (offence/defence) qboolean teamLeader; // true when this is a team leader int powerups; // so can display quad/flag status int medkitUsageTime; int invulnerabilityStartTime; int invulnerabilityStopTime; int breathPuffTime; // when clientinfo is changed, the loading of models/skins/sounds // can be deferred until you are dead, to prevent hitches in // gameplay char modelName[MAX_QPATH]; char skinName[MAX_QPATH]; char headModelName[MAX_QPATH]; char headSkinName[MAX_QPATH]; char redTeam[MAX_TEAMNAME]; char blueTeam[MAX_TEAMNAME]; qboolean deferred; qboolean newAnims; // true if using the new mission pack animations qboolean fixedlegs; // true if legs yaw is always the same as torso yaw qboolean fixedtorso; // true if torso never changes yaw vec3_t headOffset; // move head in icon views footstep_t footsteps; gender_t gender; // from model qhandle_t legsModel; qhandle_t legsSkin; qhandle_t torsoModel; qhandle_t torsoSkin; qhandle_t headModel; qhandle_t headSkin; qhandle_t modelIcon; animation_t animations[MAX_TOTALANIMATIONS]; sfxHandle_t sounds[MAX_CUSTOM_SOUNDS]; int isDead; } clientInfo_t; // each WP_* weapon enum has an associated weaponInfo_t // that contains media references necessary to present the // weapon and its effects typedef struct weaponInfo_s { qboolean registered; gitem_t *item; qhandle_t handsModel; // the hands don't actually draw, they just position the weapon qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; vec3_t weaponMidpoint; // so it will rotate centered instead of by tag float flashDlight; vec3_t flashDlightColor; sfxHandle_t flashSound[4]; // fast firing weapons randomly choose qhandle_t weaponIcon; qhandle_t ammoIcon; qhandle_t ammoModel; qhandle_t missileModel; sfxHandle_t missileSound; void (*missileTrailFunc)( centity_t *, const struct weaponInfo_s *wi ); float missileDlight; vec3_t missileDlightColor; int missileRenderfx; void (*ejectBrassFunc)( centity_t * ); float trailRadius; float wiTrailTime; sfxHandle_t readySound; sfxHandle_t firingSound; qboolean loopFireSound; } weaponInfo_t; // each IT_* item has an associated itemInfo_t // that constains media references necessary to present the // item and its effects typedef struct { qboolean registered; qhandle_t models[MAX_ITEM_MODELS]; qhandle_t icon; } itemInfo_t; typedef struct { int itemNum; } powerupInfo_t; #define MAX_SKULLTRAIL 10 typedef struct { vec3_t positions[MAX_SKULLTRAIL]; int numpositions; } skulltrail_t; #define MAX_REWARDSTACK 10 #define MAX_SOUNDBUFFER 20 //====================================================================== // all cg.stepTime, cg.duckTime, cg.landTime, etc are set to cg.time when the action // occurs, and they will have visible effects for #define STEP_TIME or whatever msec after #define MAX_PREDICTED_EVENTS 16 //unlagged - optimized prediction #define NUM_SAVED_STATES (CMD_BACKUP + 2) //unlagged - optimized prediction typedef struct { int clientFrame; // incremented each frame int clientNum; qboolean demoPlayback; qboolean levelShot; // taking a level menu screenshot int deferredPlayerLoading; qboolean loading; // don't defer players at initial startup qboolean intermissionStarted; // don't play voice rewards, because game will end shortly // there are only one or two snapshot_t that are relevent at a time int latestSnapshotNum; // the number of snapshots the client system has received int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet snapshot_t *snap; // cg.snap->serverTime <= cg.time snapshot_t *nextSnap; // cg.nextSnap->serverTime > cg.time, or NULL snapshot_t activeSnapshots[2]; float frameInterpolation; // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime) qboolean thisFrameTeleport; qboolean nextFrameTeleport; int frametime; // cg.time - cg.oldTime int time; // this is the time value that the client // is rendering at. int oldTime; // time at last frame, used for missile trails and prediction checking int physicsTime; // either cg.snap->time or cg.nextSnap->time int timelimitWarnings; // 5 min, 1 min, overtime int fraglimitWarnings; qboolean mapRestart; // set on a map restart to set back the weapon qboolean renderingThirdPerson; // during deaths, chasecams, etc // prediction state qboolean hyperspace; // true if prediction has hit a trigger_teleport playerState_t predictedPlayerState; centity_t predictedPlayerEntity; qboolean validPPS; // clear until the first call to CG_PredictPlayerState int predictedErrorTime; vec3_t predictedError; int eventSequence; int predictableEvents[MAX_PREDICTED_EVENTS]; float stepChange; // for stair up smoothing int stepTime; float duckChange; // for duck viewheight smoothing int duckTime; float landChange; // for landing hard int landTime; // input state sent to server int weaponSelect; // auto rotating items vec3_t autoAngles; vec3_t autoAxis[3]; vec3_t autoAnglesFast; vec3_t autoAxisFast[3]; // view rendering refdef_t refdef; vec3_t refdefViewAngles; // will be converted to refdef.viewaxis // zoom key qboolean zoomed; int zoomTime; float zoomSensitivity; // information screen text during loading char infoScreenText[MAX_STRING_CHARS]; // scoreboard int scoresRequestTime; int numScores; int selectedScore; int teamScores[2]; score_t scores[MAX_CLIENTS]; qboolean showScores; qboolean scoreBoardShowing; int scoreFadeTime; int accuracys[WP_NUM_WEAPONS][2]; int accRequestTime; qboolean showAcc; qboolean accBoardShowing; int accFadeTime; char killerName[MAX_NAME_LENGTH]; char spectatorList[MAX_STRING_CHARS]; // list of names int spectatorLen; // length of list float spectatorWidth; // width in device units int spectatorTime; // next time to offset int spectatorPaintX; // current paint x int spectatorPaintX2; // current paint x int spectatorOffset; // current offset from start int spectatorPaintLen; // current offset from start // skull trails skulltrail_t skulltrails[MAX_CLIENTS]; // centerprinting int centerPrintTime; int centerPrintCharWidth; int centerPrintY; char centerPrint[1024]; int centerPrintLines; // low ammo warning state int lowAmmoWarning; // 1 = low, 2 = empty // kill timers for carnage reward int lastKillTime; // crosshair client ID int crosshairClientNum; int crosshairClientTime; // powerup active flashing int powerupActive; int powerupTime; // attacking player int attackerTime; int voiceTime; // reward medals int rewardStack; int rewardTime; int rewardCount[MAX_REWARDSTACK]; qhandle_t rewardShader[MAX_REWARDSTACK]; qhandle_t rewardSound[MAX_REWARDSTACK]; // sound buffer mainly for announcer sounds int soundBufferIn; int soundBufferOut; int soundTime; qhandle_t soundBuffer[MAX_SOUNDBUFFER]; // for voice chat buffer int voiceChatTime; int voiceChatBufferIn; int voiceChatBufferOut; // warmup countdown int warmup; int warmupCount; //========================== int itemPickup; int itemPickupTime; int itemPickupBlendTime; // the pulse around the crosshair is timed seperately int weaponSelectTime; int weaponAnimation; int weaponAnimationTime; // blend blobs float damageTime; float damageX, damageY, damageValue; // status bar head float headYaw; float headEndPitch; float headEndYaw; int headEndTime; float headStartPitch; float headStartYaw; int headStartTime; // view movement float v_dmg_time; float v_dmg_pitch; float v_dmg_roll; vec3_t kick_angles; // weapon kicks vec3_t kick_origin; // temp working variables for player view float bobfracsin; int bobcycle; float xyspeed; int nextOrbitTime; //qboolean cameraMode; // if rendering from a loaded camera // development tool refEntity_t testModelEntity; char testModelName[MAX_QPATH]; qboolean testGun; //unlagged - optimized prediction int lastPredictedCommand; int lastServerTime; playerState_t savedPmoveStates[NUM_SAVED_STATES]; int stateHead, stateTail; //unlagged - optimized prediction //time that the client will respawn. If 0 = the player is alive. int respawnTime; int redObeliskHealth; int blueObeliskHealth; } cg_t; // all of the model, shader, and sound references that are // loaded at gamestate time are stored in cgMedia_t // Other media that can be tied to clients, weapons, or items are // stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t typedef struct { qhandle_t charsetShader; qhandle_t charsetProp; qhandle_t charsetPropGlow; qhandle_t charsetPropB; qhandle_t whiteShader; qhandle_t redCubeModel; qhandle_t blueCubeModel; qhandle_t redCubeIcon; qhandle_t blueCubeIcon; qhandle_t redFlagModel; qhandle_t blueFlagModel; qhandle_t neutralFlagModel; qhandle_t redFlagShader[3]; qhandle_t blueFlagShader[3]; qhandle_t flagShader[4]; //For Double Domination: //qhandle_t ddPointA; //qhandle_t ddPointB; qhandle_t ddPointSkinA[4]; //white,red,blue,none qhandle_t ddPointSkinB[4]; //white,red,blue,none qhandle_t flagPoleModel; qhandle_t flagFlapModel; qhandle_t redFlagFlapSkin; qhandle_t blueFlagFlapSkin; qhandle_t neutralFlagFlapSkin; qhandle_t redFlagBaseModel; qhandle_t blueFlagBaseModel; qhandle_t neutralFlagBaseModel; qhandle_t overloadBaseModel; qhandle_t overloadTargetModel; qhandle_t overloadLightsModel; qhandle_t overloadEnergyModel; qhandle_t harvesterModel; qhandle_t harvesterRedSkin; qhandle_t harvesterBlueSkin; qhandle_t harvesterNeutralModel; qhandle_t armorModel; qhandle_t armorIcon; qhandle_t teamStatusBar; qhandle_t deferShader; // gib explosions qhandle_t gibAbdomen; qhandle_t gibArm; qhandle_t gibChest; qhandle_t gibFist; qhandle_t gibFoot; qhandle_t gibForearm; qhandle_t gibIntestine; qhandle_t gibLeg; qhandle_t gibSkull; qhandle_t gibBrain; qhandle_t smoke2; qhandle_t machinegunBrassModel; qhandle_t shotgunBrassModel; qhandle_t railRingsShader; qhandle_t railCoreShader; qhandle_t lightningShader; qhandle_t friendShader; qhandle_t balloonShader; qhandle_t connectionShader; qhandle_t selectShader; qhandle_t viewBloodShader; qhandle_t tracerShader; qhandle_t crosshairShader[NUM_CROSSHAIRS]; qhandle_t lagometerShader; qhandle_t backTileShader; qhandle_t noammoShader; qhandle_t smokePuffShader; qhandle_t smokePuffRageProShader; qhandle_t shotgunSmokePuffShader; qhandle_t plasmaBallShader; qhandle_t waterBubbleShader; qhandle_t bloodTrailShader; // LEILEI shaders qhandle_t lsmkShader1; qhandle_t lsmkShader2; qhandle_t lsmkShader3; qhandle_t lsmkShader4; qhandle_t lbumShader1; qhandle_t lfblShader1; qhandle_t lsplShader; qhandle_t lspkShader1; qhandle_t lspkShader2; qhandle_t lbldShader1; qhandle_t lbldShader2; qhandle_t grappleShader; // leilei - grapple hook qhandle_t lmarkmetal1; qhandle_t lmarkmetal2; qhandle_t lmarkmetal3; qhandle_t lmarkmetal4; qhandle_t lmarkbullet1; qhandle_t lmarkbullet2; qhandle_t lmarkbullet3; qhandle_t lmarkbullet4; //#ifdef MISSIONPACK qhandle_t nailPuffShader; qhandle_t blueProxMine; //#endif qhandle_t numberShaders[11]; qhandle_t shadowMarkShader; qhandle_t botSkillShaders[5]; // wall mark shaders qhandle_t wakeMarkShader; qhandle_t bloodMarkShader; qhandle_t bulletMarkShader; qhandle_t burnMarkShader; qhandle_t holeMarkShader; qhandle_t energyMarkShader; // powerup shaders qhandle_t quadShader; qhandle_t redQuadShader; qhandle_t quadWeaponShader; qhandle_t invisShader; qhandle_t regenShader; qhandle_t battleSuitShader; qhandle_t battleWeaponShader; qhandle_t hastePuffShader; qhandle_t redKamikazeShader; qhandle_t blueKamikazeShader; // player overlays qhandle_t neutralOverlay; qhandle_t redOverlay; qhandle_t blueOverlay; // weapon effect models qhandle_t bulletFlashModel; qhandle_t ringFlashModel; qhandle_t dishFlashModel; qhandle_t lightningExplosionModel; // weapon effect shaders qhandle_t railExplosionShader; qhandle_t plasmaExplosionShader; qhandle_t bulletExplosionShader; qhandle_t rocketExplosionShader; qhandle_t grenadeExplosionShader; qhandle_t bfgExplosionShader; qhandle_t bloodExplosionShader; // special effects models qhandle_t teleportEffectModel; qhandle_t teleportEffectShader; //#ifdef MISSIONPACK qhandle_t kamikazeEffectModel; qhandle_t kamikazeShockWave; qhandle_t kamikazeHeadModel; qhandle_t kamikazeHeadTrail; qhandle_t guardPowerupModel; qhandle_t scoutPowerupModel; qhandle_t doublerPowerupModel; qhandle_t ammoRegenPowerupModel; qhandle_t invulnerabilityImpactModel; qhandle_t invulnerabilityJuicedModel; qhandle_t medkitUsageModel; qhandle_t dustPuffShader; qhandle_t heartShader; //#endif qhandle_t invulnerabilityPowerupModel; // scoreboard headers qhandle_t scoreboardName; qhandle_t scoreboardPing; qhandle_t scoreboardScore; qhandle_t scoreboardTime; // medals shown during gameplay qhandle_t medalImpressive; qhandle_t medalExcellent; qhandle_t medalGauntlet; qhandle_t medalDefend; qhandle_t medalAssist; qhandle_t medalCapture; // sounds sfxHandle_t quadSound; sfxHandle_t tracerSound; sfxHandle_t selectSound; sfxHandle_t useNothingSound; sfxHandle_t wearOffSound; sfxHandle_t footsteps[FOOTSTEP_TOTAL][4]; sfxHandle_t sfx_lghit1; sfxHandle_t sfx_lghit2; sfxHandle_t sfx_lghit3; sfxHandle_t sfx_ric1; sfxHandle_t sfx_ric2; sfxHandle_t sfx_ric3; sfxHandle_t sfx_railg; sfxHandle_t sfx_rockexp; sfxHandle_t sfx_plasmaexp; //#ifdef MISSIONPACK sfxHandle_t sfx_proxexp; sfxHandle_t sfx_nghit; sfxHandle_t sfx_nghitflesh; sfxHandle_t sfx_nghitmetal; sfxHandle_t sfx_chghit; sfxHandle_t sfx_chghitflesh; sfxHandle_t sfx_chghitmetal; sfxHandle_t kamikazeExplodeSound; sfxHandle_t kamikazeImplodeSound; sfxHandle_t kamikazeFarSound; sfxHandle_t useInvulnerabilitySound; sfxHandle_t invulnerabilityImpactSound1; sfxHandle_t invulnerabilityImpactSound2; sfxHandle_t invulnerabilityImpactSound3; sfxHandle_t invulnerabilityJuicedSound; sfxHandle_t obeliskHitSound1; sfxHandle_t obeliskHitSound2; sfxHandle_t obeliskHitSound3; sfxHandle_t obeliskRespawnSound; sfxHandle_t winnerSound; sfxHandle_t loserSound; sfxHandle_t youSuckSound; //#endif sfxHandle_t gibSound; sfxHandle_t gibBounce1Sound; sfxHandle_t gibBounce2Sound; sfxHandle_t gibBounce3Sound; sfxHandle_t teleInSound; sfxHandle_t teleOutSound; sfxHandle_t noAmmoSound; sfxHandle_t respawnSound; sfxHandle_t talkSound; sfxHandle_t landSound; sfxHandle_t fallSound; sfxHandle_t jumpPadSound; // LEILEI sfxHandle_t lspl1Sound; sfxHandle_t lspl2Sound; // Blood Splat Noises sfxHandle_t lspl3Sound; sfxHandle_t lbul1Sound; sfxHandle_t lbul2Sound; // Bullet Drop Noises sfxHandle_t lbul3Sound; sfxHandle_t lshl1Sound; sfxHandle_t lshl2Sound; // Shell Drop Noises sfxHandle_t lshl3Sound; // LEILEI END sfxHandle_t oneMinuteSound; sfxHandle_t fiveMinuteSound; sfxHandle_t suddenDeathSound; sfxHandle_t threeFragSound; sfxHandle_t twoFragSound; sfxHandle_t oneFragSound; sfxHandle_t hitSound; sfxHandle_t hitSoundHighArmor; sfxHandle_t hitSoundLowArmor; sfxHandle_t hitTeamSound; sfxHandle_t impressiveSound; sfxHandle_t excellentSound; sfxHandle_t deniedSound; sfxHandle_t humiliationSound; sfxHandle_t assistSound; sfxHandle_t defendSound; sfxHandle_t firstImpressiveSound; sfxHandle_t firstExcellentSound; sfxHandle_t firstHumiliationSound; sfxHandle_t takenLeadSound; sfxHandle_t tiedLeadSound; sfxHandle_t lostLeadSound; sfxHandle_t voteNow; sfxHandle_t votePassed; sfxHandle_t voteFailed; sfxHandle_t watrInSound; sfxHandle_t watrOutSound; sfxHandle_t watrUnSound; sfxHandle_t flightSound; sfxHandle_t medkitSound; sfxHandle_t weaponHoverSound; // teamplay sounds sfxHandle_t captureAwardSound; sfxHandle_t redScoredSound; sfxHandle_t blueScoredSound; sfxHandle_t redLeadsSound; sfxHandle_t blueLeadsSound; sfxHandle_t teamsTiedSound; sfxHandle_t captureYourTeamSound; sfxHandle_t captureOpponentSound; sfxHandle_t returnYourTeamSound; sfxHandle_t returnOpponentSound; sfxHandle_t takenYourTeamSound; sfxHandle_t takenOpponentSound; sfxHandle_t redFlagReturnedSound; sfxHandle_t blueFlagReturnedSound; sfxHandle_t neutralFlagReturnedSound; sfxHandle_t enemyTookYourFlagSound; sfxHandle_t enemyTookTheFlagSound; sfxHandle_t yourTeamTookEnemyFlagSound; sfxHandle_t yourTeamTookTheFlagSound; sfxHandle_t youHaveFlagSound; sfxHandle_t yourBaseIsUnderAttackSound; sfxHandle_t holyShitSound; // tournament sounds sfxHandle_t count3Sound; sfxHandle_t count2Sound; sfxHandle_t count1Sound; sfxHandle_t countFightSound; sfxHandle_t countPrepareSound; #ifdef MISSIONPACK // new stuff qhandle_t patrolShader; qhandle_t assaultShader; qhandle_t campShader; qhandle_t followShader; qhandle_t defendShader; qhandle_t teamLeaderShader; qhandle_t retrieveShader; qhandle_t escortShader; qhandle_t deathShader; qhandle_t flagShaders[3]; sfxHandle_t countPrepareTeamSound; #endif sfxHandle_t ammoregenSound; sfxHandle_t doublerSound; sfxHandle_t guardSound; sfxHandle_t scoutSound; qhandle_t cursor; qhandle_t selectCursor; qhandle_t sizeCursor; sfxHandle_t regenSound; sfxHandle_t protectSound; sfxHandle_t n_healthSound; sfxHandle_t hgrenb1aSound; sfxHandle_t hgrenb2aSound; sfxHandle_t wstbimplSound; sfxHandle_t wstbimpmSound; sfxHandle_t wstbimpdSound; sfxHandle_t wstbactvSound; } cgMedia_t; // The client game static (cgs) structure hold everything // loaded or calculated from the gamestate. It will NOT // be cleared when a tournement restart is done, allowing // all clients to begin playing instantly typedef struct { gameState_t gameState; // gamestate from server glconfig_t glconfig; // rendering configuration float screenXScale; // derived from glconfig float screenYScale; float screenXBias; int serverCommandSequence; // reliable command stream counter int processedSnapshotNum;// the number of snapshots cgame has requested qboolean localServer; // detected on startup by checking sv_running // parsed from serverinfo gametype_t gametype; int dmflags; int videoflags; int elimflags; int teamflags; int fraglimit; int capturelimit; int timelimit; int maxclients; char mapname[MAX_QPATH]; char redTeam[MAX_QPATH]; char blueTeam[MAX_QPATH]; int voteTime; int voteYes; int voteNo; qboolean voteModified; // beep whenever changed char voteString[MAX_STRING_TOKENS]; int teamVoteTime[2]; int teamVoteYes[2]; int teamVoteNo[2]; qboolean teamVoteModified[2]; // beep whenever changed char teamVoteString[2][MAX_STRING_TOKENS]; int levelStartTime; //Forced FFA int ffa_gt; //Elimination int roundStartTime; int roundtime; //CTF Elimination int attackingTeam; //Last Man Standing int lms_mode; //instantgib + nexuiz style rocket arena: int nopickup; //Double Domination DD int timetaken; //Domination int domination_points_count; char domination_points_names[MAX_DOMINATION_POINTS][MAX_DOMINATION_POINTS_NAMES]; int domination_points_status[MAX_DOMINATION_POINTS]; int scores1, scores2; // from configstrings int redflag, blueflag; // flag status from configstrings int flagStatus; qboolean newHud; // // locally derived information from gamestate // qhandle_t gameModels[MAX_MODELS]; sfxHandle_t gameSounds[MAX_SOUNDS]; int numInlineModels; qhandle_t inlineDrawModel[MAX_MODELS]; vec3_t inlineModelMidpoints[MAX_MODELS]; clientInfo_t clientinfo[MAX_CLIENTS]; // teamchat width is *3 because of embedded color codes char teamChatMsgs[TEAMCHAT_HEIGHT][TEAMCHAT_WIDTH*3+1]; int teamChatMsgTimes[TEAMCHAT_HEIGHT]; int teamChatPos; int teamLastChatPos; int cursorX; int cursorY; qboolean eventHandling; qboolean mouseCaptured; qboolean sizingHud; void *capturedItem; qhandle_t activeCursor; // orders int currentOrder; qboolean orderPending; int orderTime; int currentVoiceClient; int acceptOrderTime; int acceptTask; int acceptLeader; char acceptVoice[MAX_NAME_LENGTH]; // media cgMedia_t media; //unlagged - client options // this will be set to the server's g_delagHitscan int delagHitscan; //unlagged - client options //KK-OAX For storing whether or not the server has multikills enabled. int altExcellent; } cgs_t; //============================================================================== extern cgs_t cgs; extern cg_t cg; extern centity_t cg_entities[MAX_GENTITIES]; extern weaponInfo_t cg_weapons[MAX_WEAPONS]; extern itemInfo_t cg_items[MAX_ITEMS]; extern markPoly_t cg_markPolys[MAX_MARK_POLYS]; extern vmCvar_t cg_centertime; extern vmCvar_t cg_runpitch; extern vmCvar_t cg_runroll; extern vmCvar_t cg_bobup; extern vmCvar_t cg_bobpitch; extern vmCvar_t cg_bobroll; extern vmCvar_t cg_swingSpeed; extern vmCvar_t cg_shadows; extern vmCvar_t cg_gibs; extern vmCvar_t cg_drawTimer; extern vmCvar_t cg_drawFPS; extern vmCvar_t cg_drawSnapshot; extern vmCvar_t cg_draw3dIcons; extern vmCvar_t cg_drawIcons; extern vmCvar_t cg_drawAmmoWarning; extern vmCvar_t cg_drawCrosshair; extern vmCvar_t cg_drawCrosshairNames; extern vmCvar_t cg_drawRewards; extern vmCvar_t cg_drawTeamOverlay; extern vmCvar_t cg_teamOverlayUserinfo; extern vmCvar_t cg_crosshairX; extern vmCvar_t cg_crosshairY; extern vmCvar_t cg_crosshairSize; extern vmCvar_t cg_crosshairHealth; extern vmCvar_t cg_drawStatus; extern vmCvar_t cg_draw2D; extern vmCvar_t cg_animSpeed; extern vmCvar_t cg_debugAnim; extern vmCvar_t cg_debugPosition; extern vmCvar_t cg_debugEvents; extern vmCvar_t cg_railTrailTime; extern vmCvar_t cg_errorDecay; extern vmCvar_t cg_nopredict; extern vmCvar_t cg_noPlayerAnims; extern vmCvar_t cg_showmiss; extern vmCvar_t cg_footsteps; extern vmCvar_t cg_addMarks; extern vmCvar_t cg_brassTime; extern vmCvar_t cg_gun_frame; extern vmCvar_t cg_gun_x; extern vmCvar_t cg_gun_y; extern vmCvar_t cg_gun_z; extern vmCvar_t cg_drawGun; extern vmCvar_t cg_viewsize; extern vmCvar_t cg_tracerChance; extern vmCvar_t cg_tracerWidth; extern vmCvar_t cg_tracerLength; extern vmCvar_t cg_autoswitch; extern vmCvar_t cg_ignore; extern vmCvar_t cg_simpleItems; extern vmCvar_t cg_fov; extern vmCvar_t cg_zoomFov; extern vmCvar_t cg_thirdPersonRange; extern vmCvar_t cg_thirdPersonAngle; extern vmCvar_t cg_thirdPerson; extern vmCvar_t cg_lagometer; extern vmCvar_t cg_drawAttacker; extern vmCvar_t cg_drawSpeed; extern vmCvar_t cg_synchronousClients; extern vmCvar_t cg_teamChatTime; extern vmCvar_t cg_teamChatHeight; extern vmCvar_t cg_stats; extern vmCvar_t cg_forceModel; extern vmCvar_t cg_buildScript; extern vmCvar_t cg_paused; extern vmCvar_t cg_blood; extern vmCvar_t cg_predictItems; extern vmCvar_t cg_deferPlayers; extern vmCvar_t cg_drawFriend; extern vmCvar_t cg_teamChatsOnly; extern vmCvar_t cg_noVoiceChats; extern vmCvar_t cg_noVoiceText; extern vmCvar_t cg_scorePlum; //unlagged - smooth clients #2 // this is done server-side now //extern vmCvar_t cg_smoothClients; //unlagged - smooth clients #2 extern vmCvar_t pmove_fixed; extern vmCvar_t pmove_msec; extern vmCvar_t pmove_float; //extern vmCvar_t cg_pmove_fixed; extern vmCvar_t cg_cameraOrbit; extern vmCvar_t cg_cameraOrbitDelay; extern vmCvar_t cg_timescaleFadeEnd; extern vmCvar_t cg_timescaleFadeSpeed; extern vmCvar_t cg_timescale; extern vmCvar_t cg_cameraMode; extern vmCvar_t cg_smallFont; extern vmCvar_t cg_bigFont; extern vmCvar_t cg_noTaunt; extern vmCvar_t cg_noProjectileTrail; extern vmCvar_t cg_oldRail; extern vmCvar_t cg_oldRocket; extern vmCvar_t cg_leiEnhancement; // LEILEI'S LINE! extern vmCvar_t cg_leiGoreNoise; // LEILEI'S LINE! extern vmCvar_t cg_leiBrassNoise; // LEILEI'S LINE! extern vmCvar_t cg_leiSuperGoreyAwesome; // LEILEI'S LINE! extern vmCvar_t cg_oldPlasma; extern vmCvar_t cg_trueLightning; extern vmCvar_t cg_music; #ifdef MISSIONPACK extern vmCvar_t cg_redTeamName; extern vmCvar_t cg_blueTeamName; extern vmCvar_t cg_currentSelectedPlayer; extern vmCvar_t cg_currentSelectedPlayerName; extern vmCvar_t cg_singlePlayer; extern vmCvar_t cg_singlePlayerActive; extern vmCvar_t cg_recordSPDemo; extern vmCvar_t cg_recordSPDemoName; #endif //Sago: Moved outside extern vmCvar_t cg_obeliskRespawnDelay; extern vmCvar_t cg_enableDust; extern vmCvar_t cg_enableBreath; //unlagged - client options extern vmCvar_t cg_delag; //extern vmCvar_t cg_debugDelag; //extern vmCvar_t cg_drawBBox; extern vmCvar_t cg_cmdTimeNudge; extern vmCvar_t sv_fps; extern vmCvar_t cg_projectileNudge; extern vmCvar_t cg_optimizePrediction; extern vmCvar_t cl_timeNudge; //extern vmCvar_t cg_latentSnaps; //extern vmCvar_t cg_latentCmds; //extern vmCvar_t cg_plOut; //unlagged - client options //extra CVARS elimination extern vmCvar_t cg_alwaysWeaponBar; extern vmCvar_t cg_hitsound; extern vmCvar_t cg_voip_teamonly; extern vmCvar_t cg_voteflags; extern vmCvar_t cg_cyclegrapple; extern vmCvar_t cg_vote_custom_commands; extern vmCvar_t cg_autovertex; //Cvar to adjust the size of the fragmessage extern vmCvar_t cg_fragmsgsize; extern vmCvar_t cg_crosshairPulse; extern vmCvar_t cg_differentCrosshairs; extern vmCvar_t cg_ch1; extern vmCvar_t cg_ch1size; extern vmCvar_t cg_ch2; extern vmCvar_t cg_ch2size; extern vmCvar_t cg_ch3; extern vmCvar_t cg_ch3size; extern vmCvar_t cg_ch4; extern vmCvar_t cg_ch4size; extern vmCvar_t cg_ch5; extern vmCvar_t cg_ch5size; extern vmCvar_t cg_ch6; extern vmCvar_t cg_ch6size; extern vmCvar_t cg_ch7; extern vmCvar_t cg_ch7size; extern vmCvar_t cg_ch8; extern vmCvar_t cg_ch8size; extern vmCvar_t cg_ch9; extern vmCvar_t cg_ch9size; extern vmCvar_t cg_ch10; extern vmCvar_t cg_ch10size; extern vmCvar_t cg_ch11; extern vmCvar_t cg_ch11size; extern vmCvar_t cg_ch12; extern vmCvar_t cg_ch12size; extern vmCvar_t cg_ch13; extern vmCvar_t cg_ch13size; extern vmCvar_t cg_crosshairColorRed; extern vmCvar_t cg_crosshairColorGreen; extern vmCvar_t cg_crosshairColorBlue; extern vmCvar_t cg_weaponBarStyle; extern vmCvar_t cg_weaponOrder; extern vmCvar_t cg_chatBeep; extern vmCvar_t cg_teamChatBeep; //unlagged - cg_unlagged.c void CG_PredictWeaponEffects( centity_t *cent ); //void CG_AddBoundingBox( centity_t *cent ); qboolean CG_Cvar_ClampInt( const char *name, vmCvar_t *vmCvar, int min, int max ); //unlagged - cg_unlagged.c // // cg_main.c // const char *CG_ConfigString( int index ); const char *CG_Argv( int arg ); void QDECL CG_Printf( const char *msg, ... ); void QDECL CG_Error( const char *msg, ... ) __attribute__((noreturn)); void CG_StartMusic( void ); void CG_UpdateCvars( void ); int CG_CrosshairPlayer( void ); int CG_LastAttacker( void ); void CG_LoadMenus(const char *menuFile); void CG_KeyEvent(int key, qboolean down); void CG_MouseEvent(int x, int y); void CG_EventHandling(int type); void CG_RankRunFrame( void ); void CG_SetScoreSelection(void *menu); //score_t *CG_GetSelectedScore( void ); void CG_BuildSpectatorString( void ); //unlagged, sagos modfication void SnapVectorTowards( vec3_t v, vec3_t to ); void CG_FairCvars( void ); // // cg_view.c // void CG_TestModel_f (void); void CG_TestGun_f (void); void CG_TestModelNextFrame_f (void); void CG_TestModelPrevFrame_f (void); void CG_TestModelNextSkin_f (void); void CG_TestModelPrevSkin_f (void); void CG_ZoomDown_f( void ); void CG_ZoomUp_f( void ); void CG_AddBufferedSound( sfxHandle_t sfx); void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); // // cg_drawtools.c // void CG_AdjustFrom640( float *x, float *y, float *w, float *h ); void CG_FillRect( float x, float y, float width, float height, const float *color ); void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); void CG_DrawString( float x, float y, const char *string, float charWidth, float charHeight, const float *modulate ); void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ); void CG_DrawBigString( int x, int y, const char *s, float alpha ); void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); void CG_DrawSmallString( int x, int y, const char *s, float alpha ); void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ); int CG_DrawStrlen( const char *str ); float *CG_FadeColor( int startMsec, int totalMsec ); float *CG_TeamColor( int team ); void CG_TileClear( void ); void CG_ColorForHealth( vec4_t hcolor ); void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ); void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ); void CG_DrawSides(float x, float y, float w, float h, float size); void CG_DrawTopBottom(float x, float y, float w, float h, float size); // // cg_draw.c, cg_newDraw.c // extern int sortedTeamPlayers[TEAM_MAXOVERLAY]; extern int numSortedTeamPlayers; extern int drawTeamOverlayModificationCount; extern char systemChat[256]; extern char teamChat1[256]; extern char teamChat2[256]; void CG_AddLagometerFrameInfo( void ); void CG_AddLagometerSnapshotInfo( snapshot_t *snap ); void CG_CenterPrint( const char *str, int y, int charWidth ); void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ); void CG_DrawActive( stereoFrame_t stereoView ); void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ); void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ); void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style); int CG_Text_Width(const char *text, float scale, int limit); int CG_Text_Height(const char *text, float scale, int limit); void CG_SelectPrevPlayer( void ); void CG_SelectNextPlayer( void ); float CG_GetValue(int ownerDraw); qboolean CG_OwnerDrawVisible(int flags); void CG_RunMenuScript(char **args); void CG_ShowResponseHead( void ); void CG_SetPrintString(int type, const char *p); void CG_InitTeamChat( void ); void CG_GetTeamColor(vec4_t *color); const char *CG_GetGameStatusText( void ); const char *CG_GetKillerText( void ); void CG_Draw3DModel(float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles); void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader); void CG_CheckOrderPending( void ); const char *CG_GameTypeString( void ); qboolean CG_YourTeamHasFlag( void ); qboolean CG_OtherTeamHasFlag( void ); qhandle_t CG_StatusHandle(int task); // // cg_player.c // void CG_Player( centity_t *cent ); void CG_ResetPlayerEntity( centity_t *cent ); void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team, qboolean isMissile ); void CG_NewClientInfo( int clientNum ); sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ); // // cg_predict.c // void CG_BuildSolidList( void ); int CG_PointContents( const vec3_t point, int passEntityNum ); void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ); void CG_PredictPlayerState( void ); void CG_LoadDeferredPlayers( void ); // // cg_events.c // void CG_CheckEvents( centity_t *cent ); const char *CG_PlaceString( int rank ); void CG_EntityEvent( centity_t *cent, vec3_t position ); void CG_PainEvent( centity_t *cent, int health ); // // cg_ents.c // void CG_SetEntitySoundPosition( centity_t *cent ); void CG_AddPacketEntities( void ); void CG_Beam( centity_t *cent ); void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ); void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ); void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ); // // cg_weapons.c // void CG_NextWeapon_f( void ); void CG_PrevWeapon_f( void ); void CG_Weapon_f( void ); void CG_RegisterWeapon( int weaponNum ); void CG_RegisterItemVisuals( int itemNum ); void CG_FireWeapon( centity_t *cent ); void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ); void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ); void CG_ShotgunFire( entityState_t *es ); void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ); void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end ); void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ); void CG_AddViewWeapon (playerState_t *ps); void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ); void CG_DrawWeaponSelect( void ); void CG_DrawWeaponBar0(int count, int bits); void CG_DrawWeaponBar1(int count, int bits); void CG_DrawWeaponBar2(int count, int bits, float *color); void CG_DrawWeaponBar3(int count, int bits, float *color); void CG_DrawWeaponBar4(int count, int bits, float *color); void CG_DrawWeaponBar5(int count, int bits, float *color); void CG_DrawWeaponBar6(int count, int bits, float *color); void CG_DrawWeaponBar7(int count, int bits, float *color); void CG_OutOfAmmoChange( void ); // should this be in pmove? // // cg_marks.c // void CG_InitMarkPolys( void ); void CG_AddMarks( void ); void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir, float orientation, float r, float g, float b, float a, qboolean alphaFade, float radius, qboolean temporary ); void CG_LeiSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed); void CG_LeiSparks2 (vec3_t org, vec3_t vel, int duration, float x, float y, float speed); void CG_LeiPuff (vec3_t org, vec3_t vel, int duration, float x, float y, float speed, float size); // // cg_localents.c // void CG_InitLocalEntities( void ); localEntity_t *CG_AllocLocalEntity( void ); void CG_AddLocalEntities( void ); // // cg_effects.c // localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, float radius, float r, float g, float b, float a, float duration, int startTime, int fadeInTime, int leFlags, qhandle_t hShader ); void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ); void CG_SpawnEffect( vec3_t org ); //#ifdef MISSIONPACK void CG_KamikazeEffect( vec3_t org ); void CG_ObeliskExplode( vec3_t org, int entityNum ); void CG_ObeliskPain( vec3_t org ); void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ); void CG_InvulnerabilityJuiced( vec3_t org ); void CG_LightningBoltBeam( vec3_t start, vec3_t end ); //#endif void CG_ScorePlum( int client, vec3_t org, int score ); void CG_GibPlayer( vec3_t playerOrigin ); void CG_BigExplode( vec3_t playerOrigin ); void CG_Bleed( vec3_t origin, int entityNum ); localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, qhandle_t hModel, qhandle_t shader, int msec, qboolean isSprite ); void CG_SpurtBlood( vec3_t origin, vec3_t velocity, int hard ); // // cg_snapshot.c // void CG_ProcessSnapshots( void ); //unlagged - early transitioning void CG_TransitionEntity( centity_t *cent ); //unlagged - early transitioning // // cg_info.c // void CG_LoadingString( const char *s ); void CG_LoadingItem( int itemNum ); void CG_LoadingClient( int clientNum ); void CG_DrawInformation( void ); // // cg_scoreboard.c // qboolean CG_DrawOldScoreboard( void ); void CG_DrawOldTourneyScoreboard( void ); // // cg_challenges.c // void challenges_init(void); void challenges_save(void); unsigned int getChallenge(int challenge); void addChallenge(int challenge); // // cg_consolecmds.c // qboolean CG_ConsoleCommand( void ); void CG_InitConsoleCommands( void ); // // cg_servercmds.c // void CG_ExecuteNewServerCommands( int latestSequence ); void CG_ParseServerinfo( void ); void CG_SetConfigValues( void ); void CG_LoadVoiceChats( void ); void CG_ShaderStateChanged(void); void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ); void CG_PlayBufferedVoiceChats( void ); // // cg_playerstate.c // void CG_Respawn( void ); void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ); void CG_CheckChangedPredictableEvents( playerState_t *ps ); //=============================================== // // system traps // These functions are how the cgame communicates with the main game system // // print message on the local console void trap_Print( const char *fmt ); // abort the game void trap_Error( const char *fmt ) __attribute__((noreturn)); // milliseconds should only be used for performance tuning, never // for anything game related. Get time from the CG_DrawActiveFrame parameter int trap_Milliseconds( void ); // console variable interaction void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); void trap_Cvar_Update( vmCvar_t *vmCvar ); void trap_Cvar_Set( const char *var_name, const char *value ); void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); // ServerCommand and ConsoleCommand parameter access int trap_Argc( void ); void trap_Argv( int n, char *buffer, int bufferLength ); void trap_Args( char *buffer, int bufferLength ); // filesystem access // returns length of file int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); void trap_FS_Read( void *buffer, int len, fileHandle_t f ); void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); void trap_FS_FCloseFile( fileHandle_t f ); int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t // add commands to the local console as if they were typed in // for map changing, etc. The command is not executed immediately, // but will be executed in order the next time console commands // are processed void trap_SendConsoleCommand( const char *text ); // register a command name so the console can perform command completion. // FIXME: replace this with a normal console command "defineCommand"? void trap_AddCommand( const char *cmdName ); // send a string to the server over the network void trap_SendClientCommand( const char *s ); // force a screen update, only used during gamestate load void trap_UpdateScreen( void ); // model collision void trap_CM_LoadMap( const char *mapname ); int trap_CM_NumInlineModels( void ); clipHandle_t trap_CM_InlineModel( int index ); // 0 = world, 1+ = bmodels clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ); int trap_CM_PointContents( const vec3_t p, clipHandle_t model ); int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ); void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ); void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ); // Returns the projection of a polygon onto the solid brushes in the world int trap_CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); // normal sounds will have their volume dynamically changed as their entity // moves and the listener moves void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); void trap_S_StopLoopingSound(int entnum); // a local sound is always played full volume void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); void trap_S_ClearLoopingSounds( qboolean killall ); void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); // respatialize recalculates the volumes of sound as they should be heard by the // given entityNum and position void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); // returns buzz if not found void trap_S_StartBackgroundTrack( const char *intro, const char *loop ); // empty name stops music void trap_S_StopBackgroundTrack( void ); void trap_R_LoadWorldMap( const char *mapname ); // all media should be registered during level startup to prevent // hitches during gameplay qhandle_t trap_R_RegisterModel( const char *name ); // returns rgb axis if not found qhandle_t trap_R_RegisterSkin( const char *name ); // returns all white if not found qhandle_t trap_R_RegisterShader( const char *name ); // returns all white if not found qhandle_t trap_R_RegisterShaderNoMip( const char *name ); // returns all white if not found // a scene is built up by calls to R_ClearScene and the various R_Add functions. // Nothing is drawn until R_RenderScene is called. void trap_R_ClearScene( void ); void trap_R_AddRefEntityToScene( const refEntity_t *re ); // polys are intended for simple wall marks, not really for doing // significant construction void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ); void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int numPolys ); void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); void trap_R_RenderScene( const refdef_t *fd ); void trap_R_SetColor( const float *rgba ); // NULL = 1,1,1,1 void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ); void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); // The glconfig_t will not change during the life of a cgame. // If it needs to change, the entire cgame will be restarted, because // all the qhandle_t are then invalid. void trap_GetGlconfig( glconfig_t *glconfig ); // the gamestate should be grabbed at startup, and whenever a // configstring changes void trap_GetGameState( gameState_t *gamestate ); // cgame will poll each frame to see if a newer snapshot has arrived // that it is interested in. The time is returned seperately so that // snapshot latency can be calculated. void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ); // a snapshot get can fail if the snapshot (or the entties it holds) is so // old that it has fallen out of the client system queue qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ); // retrieve a text command from the server stream // the current snapshot will hold the number of the most recent command // qfalse can be returned if the client system handled the command // argc() / argv() can be used to examine the parameters of the command qboolean trap_GetServerCommand( int serverCommandNumber ); // returns the most recent command number that can be passed to GetUserCmd // this will always be at least one higher than the number in the current // snapshot, and it may be quite a few higher if it is a fast computer on // a lagged connection int trap_GetCurrentCmdNumber( void ); qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ); // used for the weapon select and zoom void trap_SetUserCmdValue( int stateValue, float sensitivityScale ); // aids for VM testing void testPrintInt( char *string, int i ); void testPrintFloat( char *string, float f ); int trap_MemoryRemaining( void ); void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); qboolean trap_Key_IsDown( int keynum ); int trap_Key_GetCatcher( void ); void trap_Key_SetCatcher( int catcher ); int trap_Key_GetKey( const char *binding ); typedef enum { SYSTEM_PRINT, CHAT_PRINT, TEAMCHAT_PRINT } q3print_t; // bk001201 - warning: useless keyword or type name in empty declaration int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status trap_CIN_StopCinematic(int handle); e_status trap_CIN_RunCinematic (int handle); void trap_CIN_DrawCinematic (int handle); void trap_CIN_SetExtents (int handle, int x, int y, int w, int h); void trap_SnapVector( float *v ); qboolean trap_loadCamera(const char *name); void trap_startCamera(int time); qboolean trap_getCameraInfo(int time, vec3_t *origin, vec3_t *angles); qboolean trap_GetEntityToken( char *buffer, int bufferSize ); void CG_ClearParticles (void); void CG_AddParticles (void); void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum); void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent); void CG_AddParticleShrapnel (localEntity_t *le); void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent); void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration); void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed); void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir); void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha); void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd); extern qboolean initparticles; int CG_NewParticleArea ( int num ); // LEILEI ENHANCEMENT openarena_0.8.8.orig/code/cgame/cg_localents.c0000644000175000017500000006501111656310265020021 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_localents.c -- every frame, generate renderer commands for locally // processed entities, like smoke puffs, gibs, shells, etc. #include "cg_local.h" #define MAX_LOCAL_ENTITIES 512 localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES]; localEntity_t cg_activeLocalEntities; // double linked list localEntity_t *cg_freeLocalEntities; // single linked list /* =================== CG_InitLocalEntities This is called at startup and for tournement restarts =================== */ void CG_InitLocalEntities( void ) { int i; memset( cg_localEntities, 0, sizeof( cg_localEntities ) ); cg_activeLocalEntities.next = &cg_activeLocalEntities; cg_activeLocalEntities.prev = &cg_activeLocalEntities; cg_freeLocalEntities = cg_localEntities; for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) { cg_localEntities[i].next = &cg_localEntities[i+1]; } } /* ================== CG_FreeLocalEntity ================== */ void CG_FreeLocalEntity( localEntity_t *le ) { if ( !le->prev ) { CG_Error( "CG_FreeLocalEntity: not active" ); } // remove from the doubly linked active list le->prev->next = le->next; le->next->prev = le->prev; // the free list is only singly linked le->next = cg_freeLocalEntities; cg_freeLocalEntities = le; } /* =================== CG_AllocLocalEntity Will allways succeed, even if it requires freeing an old active entity =================== */ localEntity_t *CG_AllocLocalEntity( void ) { localEntity_t *le; if ( !cg_freeLocalEntities ) { // no free entities, so free the one at the end of the chain // remove the oldest active entity CG_FreeLocalEntity( cg_activeLocalEntities.prev ); } le = cg_freeLocalEntities; cg_freeLocalEntities = cg_freeLocalEntities->next; memset( le, 0, sizeof( *le ) ); // link into the active list le->next = cg_activeLocalEntities.next; le->prev = &cg_activeLocalEntities; cg_activeLocalEntities.next->prev = le; cg_activeLocalEntities.next = le; return le; } /* ==================================================================================== FRAGMENT PROCESSING A fragment localentity interacts with the environment in some way (hitting walls), or generates more localentities along a trail. ==================================================================================== */ /* ================ CG_BloodTrail Leave expanding blood puffs behind gibs ================ */ void CG_BloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; localEntity_t *blood; step = 150; t = step * ( (cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin ); blood = CG_SmokePuff( newOrigin, vec3_origin, 20, // radius 1, 1, 1, 1, // color 2000, // trailTime t, // startTime 0, // fadeInTime 0, // flags cgs.media.bloodTrailShader ); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; // drop a total of 40 units over its lifetime blood->pos.trDelta[2] = 40; if ( cg_leiSuperGoreyAwesome.integer ) { // blood = CG_SpurtBlood( newOrigin, vec3_origin, 3); // LEILEI more gore plz } } } // LEILEI void CG_SmallBloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; localEntity_t *blood; step = 61; t = step * ( (cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin ); blood = CG_SmokePuff( newOrigin, vec3_origin, 3, // radius 1, 1, 1, 1, // color 770, // trailTime t, // startTime 0, // fadeInTime 0, // flags cgs.media.lbldShader1 ); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; // drop a total of 40 units over its lifetime blood->pos.trDelta[2] = 120; } } /* ================ CG_FragmentBounceMark ================ */ void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) { int radius; if ( le->leMarkType == LEMT_BLOOD ) { radius = 16 + (rand()&31); CG_ImpactMark( cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random()*360, 1,1,1,1, qtrue, radius, qfalse ); } else if ( le->leMarkType == LEMT_BURN ) { radius = 8 + (rand()&15); CG_ImpactMark( cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random()*360, 1,1,1,1, qtrue, radius, qfalse ); } // don't allow a fragment to make multiple marks, or they // pile up while settling le->leMarkType = LEMT_NONE; } /* ================ CG_FragmentBounceSound ================ */ void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) { if ( le->leBounceSoundType == LEBS_BLOOD ) { // half the gibs will make splat sounds if ( rand() & 1 ) { int r = rand()&3; sfxHandle_t s; if ( r == 0 ) { s = cgs.media.gibBounce1Sound; } else if ( r == 1 ) { s = cgs.media.gibBounce2Sound; } else { s = cgs.media.gibBounce3Sound; } trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); } } else if ( le->leBounceSoundType == LEBS_BRASS ) { if ( cg_leiBrassNoise.integer ) { // half the casings will make casing sounds if ( rand() & 1 ) { int r = rand()&3; sfxHandle_t s; if ( r == 0 ) { s = cgs.media.lbul1Sound; } else if ( r == 1 ) { s = cgs.media.lbul2Sound; } else { s = cgs.media.lbul3Sound; } trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); } } } else if ( le->leBounceSoundType == LEBS_SHELL ) { if ( cg_leiBrassNoise.integer ) { // half the casings will make casing sounds if ( rand() & 1 ) { int r = rand()&3; sfxHandle_t s; if ( r == 0 ) { s = cgs.media.lshl1Sound; } else if ( r == 1 ) { s = cgs.media.lshl2Sound; } else { s = cgs.media.lshl3Sound; } trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); } } } // don't allow a fragment to make multiple bounce sounds, // or it gets too noisy as they settle le->leBounceSoundType = LEBS_NONE; } // LEILEI void CG_GoreMark( localEntity_t *le, trace_t *trace ) { int radius; if ( le->leMarkType == LEMT_BURN ) { radius = 6 + (rand()&16); CG_ImpactMark( cgs.media.lbldShader2, trace->endpos, trace->plane.normal, random()*360, 1,1,1,1, qtrue, radius, qfalse ); } le->leMarkType = LEMT_NONE; } /* ================ CG_SplatSound LEILEI ================ */ void CG_SplatSound( localEntity_t *le, trace_t *trace ) { if ( le->leBounceSoundType == LEBS_BLOOD ) { // half the splats will make splat sounds if ( cg_leiGoreNoise.integer ) { if ( rand() & 1 ) { int r = rand()&3; sfxHandle_t s; if ( r == 0 ) { s = cgs.media.lspl1Sound; } else if ( r == 1 ) { s = cgs.media.lspl2Sound; } else { s = cgs.media.lspl3Sound; } trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); } } } else if ( le->leBounceSoundType == LEBS_BRASS ) { // no GERMAN EURO CENSOR ROBOTS mode yet. } // don't allow a fragment to make multiple bounce sounds, // or it gets too noisy as they settle le->leBounceSoundType = LEBS_NONE; } /* ================ CG_ReflectVelocity ================ */ void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta ); VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta ); VectorCopy( trace->endpos, le->pos.trBase ); le->pos.trTime = cg.time; // check for stop, making sure that even on low FPS systems it doesn't bobble if ( trace->allsolid || ( trace->plane.normal[2] > 0 && ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) { le->pos.trType = TR_STATIONARY; } else { } } /* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); } // LEILEI void CG_JustSplat( localEntity_t *le, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta ); VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta ); VectorCopy( trace->endpos, le->pos.trBase ); le->pos.trTime = cg.time; // check for stop, making sure that even on low FPS systems it doesn't bobble if ( trace->allsolid || ( trace->plane.normal[2] > 0 && ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) { le->pos.trType = TR_STATIONARY; } else { } } void CG_AddGore( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time //int t; //float oldZ; CG_FreeLocalEntity( le ); // kill it return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); CG_SmallBloodTrail( le ); return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_GoreMark( le, &trace ); // do a juicy sound CG_SplatSound( le, &trace ); CG_JustSplat( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); } /* ===================================================================== TRIVIAL LOCAL ENTITIES These only do simple scaling or modulation before passing to the renderer ===================================================================== */ /* ==================== CG_AddFadeRGB ==================== */ void CG_AddFadeRGB( localEntity_t *le ) { refEntity_t *re; float c; re = &le->refEntity; c = ( le->endTime - cg.time ) * le->lifeRate; c *= 0xff; re->shaderRGBA[0] = le->color[0] * c; re->shaderRGBA[1] = le->color[1] * c; re->shaderRGBA[2] = le->color[2] * c; re->shaderRGBA[3] = le->color[3] * c; trap_R_AddRefEntityToScene( re ); } /* ================== CG_AddMoveScaleFade ================== */ static void CG_AddMoveScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { // fade / grow time c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime ); } else { // fade / grow time c = ( le->endTime - cg.time ) * le->lifeRate; } re->shaderRGBA[3] = 0xff * c * le->color[3]; if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { re->radius = le->radius * ( 1.0 - c ) + 8; } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); } /* =================== CG_AddScaleFade For rocket smokes that hang in place, fade out, and are removed if the view passes through them. There are often many of these, so it needs to be simple. =================== */ static void CG_AddScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade / grow time c = ( le->endTime - cg.time ) * le->lifeRate; re->shaderRGBA[3] = 0xff * c * le->color[3]; re->radius = le->radius * ( 1.0 - c ) + 8; // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); // LEILEI if (!cg_leiEnhancement.integer) { if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } } trap_R_AddRefEntityToScene( re ); } /* ================= CG_AddFallScaleFade This is just an optimized CG_AddMoveScaleFade For blood mists that drift down, fade out, and are removed if the view passes through them. There are often 100+ of these, so it needs to be simple. ================= */ static void CG_AddFallScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade time c = ( le->endTime - cg.time ) * le->lifeRate; re->shaderRGBA[3] = 0xff * c * le->color[3]; re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2]; re->radius = le->radius * ( 1.0 - c ) + 16; // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); // LEILEI if (!cg_leiEnhancement.integer) { if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } } trap_R_AddRefEntityToScene( re ); } /* ================ CG_AddExplosion ================ */ static void CG_AddExplosion( localEntity_t *ex ) { refEntity_t *ent; ent = &ex->refEntity; // add the entity trap_R_AddRefEntityToScene(ent); // add the dlight if ( ex->light ) { float light; light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime ); if ( light < 0.5 ) { light = 1.0; } else { light = 1.0 - ( light - 0.5 ) * 2; } light = ex->light * light; trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] ); } } /* ================ CG_AddSpriteExplosion ================ */ static void CG_AddSpriteExplosion( localEntity_t *le ) { refEntity_t re; float c; re = le->refEntity; c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime ); if ( c > 1 ) { c = 1.0; // can happen during connection problems } re.shaderRGBA[0] = 0xff; re.shaderRGBA[1] = 0xff; re.shaderRGBA[2] = 0xff; re.shaderRGBA[3] = 0xff * c * 0.33; re.reType = RT_SPRITE; re.radius = 42 * ( 1.0 - c ) + 30; trap_R_AddRefEntityToScene( &re ); // add the dlight if ( le->light ) { float light; light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime ); if ( light < 0.5 ) { light = 1.0; } else { light = 1.0 - ( light - 0.5 ) * 2; } light = le->light * light; trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] ); } } //#ifdef MISSIONPACK /* ==================== CG_AddKamikaze ==================== */ void CG_AddKamikaze( localEntity_t *le ) { refEntity_t *re; refEntity_t shockwave; float c; vec3_t test, axis[3]; int t; re = &le->refEntity; t = cg.time - le->startTime; VectorClear( test ); AnglesToAxis( test, axis ); if (t > KAMI_SHOCKWAVE_STARTTIME && t < KAMI_SHOCKWAVE_ENDTIME) { if (!(le->leFlags & LEF_SOUND1)) { // trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeExplodeSound ); trap_S_StartLocalSound(cgs.media.kamikazeExplodeSound, CHAN_AUTO); le->leFlags |= LEF_SOUND1; } // 1st kamikaze shockwave memset(&shockwave, 0, sizeof(shockwave)); shockwave.hModel = cgs.media.kamikazeShockWave; shockwave.reType = RT_MODEL; shockwave.shaderTime = re->shaderTime; VectorCopy(re->origin, shockwave.origin); c = (float)(t - KAMI_SHOCKWAVE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME); VectorScale( axis[0], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); VectorScale( axis[1], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); VectorScale( axis[2], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); shockwave.nonNormalizedAxes = qtrue; if (t > KAMI_SHOCKWAVEFADE_STARTTIME) { c = (float)(t - KAMI_SHOCKWAVEFADE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVEFADE_STARTTIME); } else { c = 0; } c *= 0xff; shockwave.shaderRGBA[0] = 0xff - c; shockwave.shaderRGBA[1] = 0xff - c; shockwave.shaderRGBA[2] = 0xff - c; shockwave.shaderRGBA[3] = 0xff - c; trap_R_AddRefEntityToScene( &shockwave ); } if (t > KAMI_EXPLODE_STARTTIME && t < KAMI_IMPLODE_ENDTIME) { // explosion and implosion c = ( le->endTime - cg.time ) * le->lifeRate; c *= 0xff; re->shaderRGBA[0] = le->color[0] * c; re->shaderRGBA[1] = le->color[1] * c; re->shaderRGBA[2] = le->color[2] * c; re->shaderRGBA[3] = le->color[3] * c; if( t < KAMI_IMPLODE_STARTTIME ) { c = (float)(t - KAMI_EXPLODE_STARTTIME) / (float)(KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME); } else { if (!(le->leFlags & LEF_SOUND2)) { // trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeImplodeSound ); trap_S_StartLocalSound(cgs.media.kamikazeImplodeSound, CHAN_AUTO); le->leFlags |= LEF_SOUND2; } c = (float)(KAMI_IMPLODE_ENDTIME - t) / (float) (KAMI_IMPLODE_ENDTIME - KAMI_IMPLODE_STARTTIME); } VectorScale( axis[0], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[0] ); VectorScale( axis[1], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[1] ); VectorScale( axis[2], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[2] ); re->nonNormalizedAxes = qtrue; trap_R_AddRefEntityToScene( re ); // add the dlight trap_R_AddLightToScene( re->origin, c * 1000.0, 1.0, 1.0, c ); } if (t > KAMI_SHOCKWAVE2_STARTTIME && t < KAMI_SHOCKWAVE2_ENDTIME) { // 2nd kamikaze shockwave if (le->angles.trBase[0] == 0 && le->angles.trBase[1] == 0 && le->angles.trBase[2] == 0) { le->angles.trBase[0] = random() * 360; le->angles.trBase[1] = random() * 360; le->angles.trBase[2] = random() * 360; } else { c = 0; } memset(&shockwave, 0, sizeof(shockwave)); shockwave.hModel = cgs.media.kamikazeShockWave; shockwave.reType = RT_MODEL; shockwave.shaderTime = re->shaderTime; VectorCopy(re->origin, shockwave.origin); test[0] = le->angles.trBase[0]; test[1] = le->angles.trBase[1]; test[2] = le->angles.trBase[2]; AnglesToAxis( test, axis ); c = (float)(t - KAMI_SHOCKWAVE2_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2_STARTTIME); VectorScale( axis[0], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); VectorScale( axis[1], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); VectorScale( axis[2], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); shockwave.nonNormalizedAxes = qtrue; if (t > KAMI_SHOCKWAVE2FADE_STARTTIME) { c = (float)(t - KAMI_SHOCKWAVE2FADE_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2FADE_STARTTIME); } else { c = 0; } c *= 0xff; shockwave.shaderRGBA[0] = 0xff - c; shockwave.shaderRGBA[1] = 0xff - c; shockwave.shaderRGBA[2] = 0xff - c; shockwave.shaderRGBA[3] = 0xff - c; trap_R_AddRefEntityToScene( &shockwave ); } } /* =================== CG_AddInvulnerabilityImpact =================== */ void CG_AddInvulnerabilityImpact( localEntity_t *le ) { trap_R_AddRefEntityToScene( &le->refEntity ); } /* =================== CG_AddInvulnerabilityJuiced =================== */ void CG_AddInvulnerabilityJuiced( localEntity_t *le ) { int t; t = cg.time - le->startTime; if ( t > 3000 ) { le->refEntity.axis[0][0] = (float) 1.0 + 0.3 * (t - 3000) / 2000; le->refEntity.axis[1][1] = (float) 1.0 + 0.3 * (t - 3000) / 2000; le->refEntity.axis[2][2] = (float) 0.7 + 0.3 * (2000 - (t - 3000)) / 2000; } if ( t > 5000 ) { le->endTime = 0; CG_GibPlayer( le->refEntity.origin ); } else { trap_R_AddRefEntityToScene( &le->refEntity ); } } /* =================== CG_AddRefEntity =================== */ void CG_AddRefEntity( localEntity_t *le ) { if (le->endTime < cg.time) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( &le->refEntity ); } //#endif /* =================== CG_AddScorePlum =================== */ #define NUMBER_SIZE 8 void CG_AddScorePlum( localEntity_t *le ) { refEntity_t *re; vec3_t origin, delta, dir, vec, up = {0, 0, 1}; float c, len; int i, score, digits[10], numdigits, negative; re = &le->refEntity; c = ( le->endTime - cg.time ) * le->lifeRate; score = le->radius; if (score < 0) { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0x11; re->shaderRGBA[2] = 0x11; } else { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; if (score >= 50) { re->shaderRGBA[1] = 0; } else if (score >= 20) { re->shaderRGBA[0] = re->shaderRGBA[1] = 0; } else if (score >= 10) { re->shaderRGBA[2] = 0; } else if (score >= 2) { re->shaderRGBA[0] = re->shaderRGBA[2] = 0; } } if (c < 0.25) re->shaderRGBA[3] = 0xff * 4 * c; else re->shaderRGBA[3] = 0xff; re->radius = NUMBER_SIZE / 2; VectorCopy(le->pos.trBase, origin); origin[2] += 110 - c * 100; VectorSubtract(cg.refdef.vieworg, origin, dir); CrossProduct(dir, up, vec); VectorNormalize(vec); VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < 20 ) { CG_FreeLocalEntity( le ); return; } negative = qfalse; if (score < 0) { negative = qtrue; score = -score; } for (numdigits = 0; !(numdigits && !score); numdigits++) { digits[numdigits] = score % 10; score = score / 10; } if (negative) { digits[numdigits] = 10; numdigits++; } for (i = 0; i < numdigits; i++) { VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin); re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]]; trap_R_AddRefEntityToScene( re ); } } //============================================================================== /* =================== CG_AddLocalEntities =================== */ void CG_AddLocalEntities( void ) { localEntity_t *le, *next; // walk the list backwards, so any new local entities generated // (trails, marks, etc) will be present this frame le = cg_activeLocalEntities.prev; for ( ; le != &cg_activeLocalEntities ; le = next ) { // grab next now, so if the local entity is freed we // still have it next = le->prev; if ( cg.time >= le->endTime ) { CG_FreeLocalEntity( le ); continue; } switch ( le->leType ) { default: CG_Error( "Bad leType: %i", le->leType ); break; case LE_MARK: break; case LE_SPRITE_EXPLOSION: CG_AddSpriteExplosion( le ); break; case LE_EXPLOSION: CG_AddExplosion( le ); break; case LE_FRAGMENT: // gibs and brass CG_AddFragment( le ); break; case LE_MOVE_SCALE_FADE: // water bubbles CG_AddMoveScaleFade( le ); break; case LE_FADE_RGB: // teleporters, railtrails CG_AddFadeRGB( le ); break; case LE_FALL_SCALE_FADE: // gib blood trails CG_AddFallScaleFade( le ); break; case LE_SCALE_FADE: // rocket trails CG_AddScaleFade( le ); break; case LE_SCOREPLUM: CG_AddScorePlum( le ); break; //#ifdef MISSIONPACK case LE_KAMIKAZE: CG_AddKamikaze( le ); break; case LE_INVULIMPACT: CG_AddInvulnerabilityImpact( le ); break; case LE_INVULJUICED: CG_AddInvulnerabilityJuiced( le ); break; case LE_SHOWREFENTITY: CG_AddRefEntity( le ); break; //#endif case LE_GORE: // blood CG_AddGore( le ); break; } } } openarena_0.8.8.orig/code/cgame/cg_ents.c0000644000175000017500000007346211656310265017017 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_ents.c -- present snapshot entities, happens every single frame #include "cg_local.h" /* ====================== CG_PositionEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis ); entity->backlerp = parent->backlerp; } /* ====================== CG_PositionRotatedEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; vec3_t tempAxis[3]; //AxisClear( entity->axis ); // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( entity->axis, lerped.axis, tempAxis ); MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis ); } /* ========================================================================== FUNCTIONS CALLED EACH FRAME ========================================================================== */ /* ====================== CG_SetEntitySoundPosition Also called by event processing code ====================== */ void CG_SetEntitySoundPosition( centity_t *cent ) { if ( cent->currentState.solid == SOLID_BMODEL ) { vec3_t origin; float *v; v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, origin ); trap_S_UpdateEntityPosition( cent->currentState.number, origin ); } else { trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin ); } } /* ================== CG_EntityEffects Add continuous entity effects, like local entity emission and lighting ================== */ static void CG_EntityEffects( centity_t *cent ) { // update sound origins CG_SetEntitySoundPosition( cent ); // add loop sound if ( cent->currentState.loopSound ) { if (cent->currentState.eType != ET_SPEAKER) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] ); } else { trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] ); } } // constant light glow if(cent->currentState.constantLight) { int cl; float i, r, g, b; cl = cent->currentState.constantLight; r = (float) (cl & 0xFF) / 255.0; g = (float) ((cl >> 8) & 0xFF) / 255.0; b = (float) ((cl >> 16) & 0xFF) / 255.0; i = (float) ((cl >> 24) & 0xFF) * 4.0; trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b); } } /* ================== CG_General ================== */ static void CG_General( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // if set to invisible, skip if (!s1->modelindex) { return; } memset (&ent, 0, sizeof(ent)); // set frame ent.frame = s1->frame; ent.oldframe = ent.frame; ent.backlerp = 0; VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); ent.hModel = cgs.gameModels[s1->modelindex]; // player model if (s1->number == cg.snap->ps.clientNum) { ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors } // convert angles to axis AnglesToAxis( cent->lerpAngles, ent.axis ); // add to refresh list trap_R_AddRefEntityToScene (&ent); } /* ================== CG_Speaker Speaker entities can automatically play sounds ================== */ static void CG_Speaker( centity_t *cent ) { if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum... return; // not auto triggering } if ( cg.time < cent->miscTime ) { return; } trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] ); // ent->s.frame = ent->wait * 10; // ent->s.clientNum = ent->random * 10; cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom(); } /* ================== CG_Item ================== */ static void CG_Item( centity_t *cent ) { refEntity_t ent; entityState_t *es; gitem_t *item; int msec; float frac; float scale; weaponInfo_t *wi; es = ¢->currentState; if ( es->modelindex >= bg_numItems ) { CG_Error( "Bad item index %i on entity", es->modelindex ); } // if set to invisible, skip if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) { return; } item = &bg_itemlist[ es->modelindex ]; if ( cg_simpleItems.integer && item->giType != IT_TEAM ) { memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; VectorCopy( cent->lerpOrigin, ent.origin ); ent.radius = 14; ent.customShader = cg_items[es->modelindex].icon; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 255; trap_R_AddRefEntityToScene(&ent); return; } // items bob up and down continuously scale = 0.005 + cent->currentState.number * 0.00001; cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4; memset (&ent, 0, sizeof(ent)); // autorotate at one of two speeds if ( item->giType == IT_HEALTH ) { VectorCopy( cg.autoAnglesFast, cent->lerpAngles ); AxisCopy( cg.autoAxisFast, ent.axis ); } else { VectorCopy( cg.autoAngles, cent->lerpAngles ); AxisCopy( cg.autoAxis, ent.axis ); } wi = NULL; // the weapons have their origin where they attatch to player // models, so we need to offset them or they will rotate // eccentricly if ( item->giType == IT_WEAPON ) { wi = &cg_weapons[item->giTag]; cent->lerpOrigin[0] -= wi->weaponMidpoint[0] * ent.axis[0][0] + wi->weaponMidpoint[1] * ent.axis[1][0] + wi->weaponMidpoint[2] * ent.axis[2][0]; cent->lerpOrigin[1] -= wi->weaponMidpoint[0] * ent.axis[0][1] + wi->weaponMidpoint[1] * ent.axis[1][1] + wi->weaponMidpoint[2] * ent.axis[2][1]; cent->lerpOrigin[2] -= wi->weaponMidpoint[0] * ent.axis[0][2] + wi->weaponMidpoint[1] * ent.axis[1][2] + wi->weaponMidpoint[2] * ent.axis[2][2]; cent->lerpOrigin[2] += 8; // an extra height boost } ent.hModel = cg_items[es->modelindex].models[0]; VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); ent.nonNormalizedAxes = qfalse; // if just respawned, slowly scale up msec = cg.time - cent->miscTime; if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) { frac = (float)msec / ITEM_SCALEUP_TIME; VectorScale( ent.axis[0], frac, ent.axis[0] ); VectorScale( ent.axis[1], frac, ent.axis[1] ); VectorScale( ent.axis[2], frac, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } else { frac = 1.0; } // items without glow textures need to keep a minimum light value // so they are always visible if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) { ent.renderfx |= RF_MINLIGHT; } // increase the size of the weapons when they are presented as items if ( item->giType == IT_WEAPON ) { VectorScale( ent.axis[0], 1.5, ent.axis[0] ); VectorScale( ent.axis[1], 1.5, ent.axis[1] ); VectorScale( ent.axis[2], 1.5, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound ); } if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) { VectorScale( ent.axis[0], 2, ent.axis[0] ); VectorScale( ent.axis[1], 2, ent.axis[1] ); VectorScale( ent.axis[2], 2, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } // add to refresh list trap_R_AddRefEntityToScene(&ent); if ( item->giType == IT_WEAPON && wi->barrelModel ) { refEntity_t barrel; memset( &barrel, 0, sizeof( barrel ) ); barrel.hModel = wi->barrelModel; VectorCopy( ent.lightingOrigin, barrel.lightingOrigin ); barrel.shadowPlane = ent.shadowPlane; barrel.renderfx = ent.renderfx; CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" ); AxisCopy( ent.axis, barrel.axis ); barrel.nonNormalizedAxes = ent.nonNormalizedAxes; trap_R_AddRefEntityToScene( &barrel ); } // accompanying rings / spheres for powerups if ( !cg_simpleItems.integer ) { vec3_t spinAngles; VectorClear( spinAngles ); if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP ) { if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 ) { if ( item->giType == IT_POWERUP ) { ent.origin[2] += 12; spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f; } AnglesToAxis( spinAngles, ent.axis ); // scale up if respawning if ( frac != 1.0 ) { VectorScale( ent.axis[0], frac, ent.axis[0] ); VectorScale( ent.axis[1], frac, ent.axis[1] ); VectorScale( ent.axis[2], frac, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } trap_R_AddRefEntityToScene( &ent ); } } } } //============================================================================ /* =============== CG_Missile =============== */ static void CG_Missile( centity_t *cent ) { refEntity_t ent; entityState_t *s1; const weaponInfo_t *weapon; // int col; s1 = ¢->currentState; if ( s1->weapon >= WP_NUM_WEAPONS ) { s1->weapon = 0; } weapon = &cg_weapons[s1->weapon]; // calculate the axis VectorCopy( s1->angles, cent->lerpAngles); // add trails if ( weapon->missileTrailFunc ) { weapon->missileTrailFunc( cent, weapon ); } /* if ( cent->currentState.modelindex == TEAM_RED ) { col = 1; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { col = 2; } else { col = 0; } // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] ); } */ // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); } // add missile sound if ( weapon->missileSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound ); } // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); if ( cent->currentState.weapon == WP_PLASMAGUN ) { ent.reType = RT_SPRITE; ent.radius = 16; ent.rotation = 0; ent.customShader = cgs.media.plasmaBallShader; trap_R_AddRefEntityToScene( &ent ); return; } // flicker between two skins ent.skinNum = cg.clientFrame & 1; ent.hModel = weapon->missileModel; ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; //#ifdef MISSIONPACK if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) { if (s1->generic1 == TEAM_BLUE) { ent.hModel = cgs.media.blueProxMine; } } //#endif // convert direction of travel into axis if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { ent.axis[0][2] = 1; } // spin as it moves if ( s1->pos.trType != TR_STATIONARY ) { RotateAroundDirection( ent.axis, cg.time / 4 ); } else { //#ifdef MISSIONPACK if ( s1->weapon == WP_PROX_LAUNCHER ) { AnglesToAxis( cent->lerpAngles, ent.axis ); } else //#endif { RotateAroundDirection( ent.axis, s1->time ); } } // add to refresh list, possibly with quad glow CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE, qtrue ); } /* =============== CG_Grapple This is called when the grapple is sitting up against the wall =============== */ static void CG_Grapple( centity_t *cent ) { refEntity_t ent; entityState_t *s1; const weaponInfo_t *weapon; s1 = ¢->currentState; if ( s1->weapon >= WP_NUM_WEAPONS ) { s1->weapon = 0; } weapon = &cg_weapons[s1->weapon]; // calculate the axis VectorCopy( s1->angles, cent->lerpAngles); // FIXME add grapple pull sound here..? // add missile sound if ( weapon->missileSound ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound ); } // Will draw cable if needed CG_GrappleTrail ( cent, weapon ); // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); // flicker between two skins ent.skinNum = cg.clientFrame & 1; ent.hModel = weapon->missileModel; ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; // convert direction of travel into axis if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { ent.axis[0][2] = 1; } trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_Mover =============== */ static void CG_Mover( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); AnglesToAxis( cent->lerpAngles, ent.axis ); ent.renderfx = RF_NOSHADOW; // flicker between two skins (FIXME?) ent.skinNum = ( cg.time >> 6 ) & 1; // get the model, either as a bmodel or a modelindex if ( s1->solid == SOLID_BMODEL ) { ent.hModel = cgs.inlineDrawModel[s1->modelindex]; } else { ent.hModel = cgs.gameModels[s1->modelindex]; } // add to refresh list trap_R_AddRefEntityToScene(&ent); // add the secondary model if ( s1->modelindex2 ) { ent.skinNum = 0; ent.hModel = cgs.gameModels[s1->modelindex2]; trap_R_AddRefEntityToScene(&ent); } } /* =============== CG_Beam Also called as an event =============== */ void CG_Beam( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( s1->pos.trBase, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); AxisClear( ent.axis ); ent.reType = RT_BEAM; ent.renderfx = RF_NOSHADOW; // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* =============== CG_Portal =============== */ static void CG_Portal( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); ByteToDir( s1->eventParm, ent.axis[0] ); PerpendicularVector( ent.axis[1], ent.axis[0] ); // negating this tends to get the directions like they want // we really should have a camera roll value VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] ); CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); ent.reType = RT_PORTALSURFACE; ent.oldframe = s1->powerups; ent.frame = s1->frame; // rotation speed ent.skinNum = s1->clientNum/256.0 * 360; // roll offset // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* ========================= CG_AdjustPositionForMover Also called by client movement prediction code ========================= */ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) { centity_t *cent; vec3_t oldOrigin, origin, deltaOrigin; vec3_t oldAngles, angles, deltaAngles; if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) { VectorCopy( in, out ); return; } cent = &cg_entities[ moverNum ]; if ( cent->currentState.eType != ET_MOVER ) { VectorCopy( in, out ); return; } BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles ); BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin ); BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles ); VectorSubtract( origin, oldOrigin, deltaOrigin ); VectorSubtract( angles, oldAngles, deltaAngles ); VectorAdd( in, deltaOrigin, out ); // FIXME: origin change when on a rotating object } /* ============================= CG_InterpolateEntityPosition ============================= */ static void CG_InterpolateEntityPosition( centity_t *cent ) { vec3_t current, next; float f; // it would be an internal error to find an entity that interpolates without // a snapshot ahead of the current one if ( cg.nextSnap == NULL ) { CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" ); } f = cg.frameInterpolation; // this will linearize a sine or parabolic curve, but it is important // to not extrapolate player positions if more recent data is available BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next ); cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next ); cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); } /* =============== CG_CalcEntityLerpPositions =============== */ static void CG_CalcEntityLerpPositions( centity_t *cent ) { //unlagged - projectile nudge // this will be set to how far forward projectiles will be extrapolated int timeshift = 0; //unlagged - projectile nudge //unlagged - smooth clients #2 // this is done server-side now - cg_smoothClients is undefined // players will always be TR_INTERPOLATE /* // if this player does not want to see extrapolated players if ( !cg_smoothClients.integer ) { // make sure the clients use TR_INTERPOLATE if ( cent->currentState.number < MAX_CLIENTS ) { cent->currentState.pos.trType = TR_INTERPOLATE; cent->nextState.pos.trType = TR_INTERPOLATE; } } */ //unlagged - smooth clients #2 if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) { CG_InterpolateEntityPosition( cent ); return; } // first see if we can interpolate between two snaps for // linear extrapolated clients if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP && cent->currentState.number < MAX_CLIENTS) { CG_InterpolateEntityPosition( cent ); return; } //unlagged - timenudge extrapolation // interpolating failed (probably no nextSnap), so extrapolate // this can also happen if the teleport bit is flipped, but that // won't be noticeable if ( cent->currentState.number < MAX_CLIENTS && cent->currentState.clientNum != cg.predictedPlayerState.clientNum ) { cent->currentState.pos.trType = TR_LINEAR_STOP; cent->currentState.pos.trTime = cg.snap->serverTime; cent->currentState.pos.trDuration = 1000 / sv_fps.integer; } //unlagged - timenudge extrapolation //unlagged - projectile nudge // if it's a missile but not a grappling hook if ( cent->currentState.eType == ET_MISSILE && cent->currentState.weapon != WP_GRAPPLING_HOOK ) { // if it's one of ours if ( cent->currentState.otherEntityNum == cg.clientNum ) { // extrapolate one server frame's worth - this will correct for tiny // visual inconsistencies introduced by backward-reconciling all players // one server frame before running projectiles timeshift = 1000 / sv_fps.integer; } // if it's not, and it's not a grenade launcher else if ( cent->currentState.weapon != WP_GRENADE_LAUNCHER ) { // extrapolate based on cg_projectileNudge timeshift = cg_projectileNudge.integer + 1000 / sv_fps.integer; } } // just use the current frame and evaluate as best we can // BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); // BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); BG_EvaluateTrajectory( ¢->currentState.pos, cg.time + timeshift, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time + timeshift, cent->lerpAngles ); // if there's a time shift if ( timeshift != 0 ) { trace_t tr; vec3_t lastOrigin; BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, lastOrigin ); CG_Trace( &tr, lastOrigin, vec3_origin, vec3_origin, cent->lerpOrigin, cent->currentState.number, MASK_SHOT ); // don't let the projectile go through the floor if ( tr.fraction < 1.0f ) { cent->lerpOrigin[0] = lastOrigin[0] + tr.fraction * ( cent->lerpOrigin[0] - lastOrigin[0] ); cent->lerpOrigin[1] = lastOrigin[1] + tr.fraction * ( cent->lerpOrigin[1] - lastOrigin[1] ); cent->lerpOrigin[2] = lastOrigin[2] + tr.fraction * ( cent->lerpOrigin[2] - lastOrigin[2] ); } } //unlagged - projectile nudge // adjust for riding a mover if it wasn't rolled into the predicted // player state if ( cent != &cg.predictedPlayerEntity ) { CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, cg.snap->serverTime, cg.time, cent->lerpOrigin ); } } /* =============== CG_TeamBase =============== */ static void CG_TeamBase( centity_t *cent ) { refEntity_t model; //#ifdef MISSIONPACK vec3_t angles; int t, h; float c; if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) { //#else // if ( cgs.gametype == GT_CTF) { //#endif // show the flag base memset(&model, 0, sizeof(model)); model.reType = RT_MODEL; VectorCopy( cent->lerpOrigin, model.lightingOrigin ); VectorCopy( cent->lerpOrigin, model.origin ); AnglesToAxis( cent->currentState.angles, model.axis ); if ( cent->currentState.modelindex == TEAM_RED ) { model.hModel = cgs.media.redFlagBaseModel; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { model.hModel = cgs.media.blueFlagBaseModel; } else { model.hModel = cgs.media.neutralFlagBaseModel; } trap_R_AddRefEntityToScene( &model ); } //#ifdef MISSIONPACK else if ( cgs.gametype == GT_OBELISK ) { // show the obelisk memset(&model, 0, sizeof(model)); model.reType = RT_MODEL; VectorCopy( cent->lerpOrigin, model.lightingOrigin ); VectorCopy( cent->lerpOrigin, model.origin ); AnglesToAxis( cent->currentState.angles, model.axis ); model.hModel = cgs.media.overloadBaseModel; trap_R_AddRefEntityToScene( &model ); // if hit if ( cent->currentState.frame == 1) { // show hit model // modelindex2 is the health value of the obelisk c = cent->currentState.modelindex2; model.shaderRGBA[0] = 0xff; model.shaderRGBA[1] = c; model.shaderRGBA[2] = c; model.shaderRGBA[3] = 0xff; // model.hModel = cgs.media.overloadEnergyModel; trap_R_AddRefEntityToScene( &model ); } // if respawning if ( cent->currentState.frame == 2) { if ( !cent->miscTime ) { cent->miscTime = cg.time; } t = cg.time - cent->miscTime; h = (cg_obeliskRespawnDelay.integer - 5) * 1000; // if (t > h) { c = (float) (t - h) / h; if (c > 1) c = 1; } else { c = 0; } // show the lights AnglesToAxis( cent->currentState.angles, model.axis ); // model.shaderRGBA[0] = c * 0xff; model.shaderRGBA[1] = c * 0xff; model.shaderRGBA[2] = c * 0xff; model.shaderRGBA[3] = c * 0xff; model.hModel = cgs.media.overloadLightsModel; trap_R_AddRefEntityToScene( &model ); // show the target if (t > h) { if ( !cent->muzzleFlashTime ) { trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY, cgs.media.obeliskRespawnSound); cent->muzzleFlashTime = 1; } VectorCopy(cent->currentState.angles, angles); angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI; AnglesToAxis( angles, model.axis ); VectorScale( model.axis[0], c, model.axis[0]); VectorScale( model.axis[1], c, model.axis[1]); VectorScale( model.axis[2], c, model.axis[2]); model.shaderRGBA[0] = 0xff; model.shaderRGBA[1] = 0xff; model.shaderRGBA[2] = 0xff; model.shaderRGBA[3] = 0xff; // model.origin[2] += 56; model.hModel = cgs.media.overloadTargetModel; trap_R_AddRefEntityToScene( &model ); } else { //FIXME: show animated smoke } } else { cent->miscTime = 0; cent->muzzleFlashTime = 0; // modelindex2 is the health value of the obelisk c = cent->currentState.modelindex2; model.shaderRGBA[0] = 0xff; model.shaderRGBA[1] = c; model.shaderRGBA[2] = c; model.shaderRGBA[3] = 0xff; // show the lights model.hModel = cgs.media.overloadLightsModel; trap_R_AddRefEntityToScene( &model ); // show the target model.origin[2] += 56; model.hModel = cgs.media.overloadTargetModel; trap_R_AddRefEntityToScene( &model ); } } else if ( cgs.gametype == GT_HARVESTER ) { // show harvester model memset(&model, 0, sizeof(model)); model.reType = RT_MODEL; VectorCopy( cent->lerpOrigin, model.lightingOrigin ); VectorCopy( cent->lerpOrigin, model.origin ); AnglesToAxis( cent->currentState.angles, model.axis ); if ( cent->currentState.modelindex == TEAM_RED ) { model.hModel = cgs.media.harvesterModel; model.customSkin = cgs.media.harvesterRedSkin; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { model.hModel = cgs.media.harvesterModel; model.customSkin = cgs.media.harvesterBlueSkin; } else { model.hModel = cgs.media.harvesterNeutralModel; model.customSkin = 0; } trap_R_AddRefEntityToScene( &model ); } //#endif } /* =============== CG_AddCEntity =============== */ static void CG_AddCEntity( centity_t *cent ) { // event-only entities will have been dealt with already if ( cent->currentState.eType >= ET_EVENTS ) { return; } // calculate the current origin CG_CalcEntityLerpPositions( cent ); // add automatic effects CG_EntityEffects( cent ); switch ( cent->currentState.eType ) { default: CG_Error( "Bad entity type: %i\n", cent->currentState.eType ); break; case ET_INVISIBLE: case ET_PUSH_TRIGGER: case ET_TELEPORT_TRIGGER: break; case ET_GENERAL: CG_General( cent ); break; case ET_PLAYER: CG_Player( cent ); break; case ET_ITEM: CG_Item( cent ); break; case ET_MISSILE: CG_Missile( cent ); break; case ET_MOVER: CG_Mover( cent ); break; case ET_BEAM: CG_Beam( cent ); break; case ET_PORTAL: CG_Portal( cent ); break; case ET_SPEAKER: CG_Speaker( cent ); break; case ET_GRAPPLE: CG_Grapple( cent ); break; case ET_TEAM: CG_TeamBase( cent ); break; } } /* =============== CG_AddPacketEntities =============== */ void CG_AddPacketEntities( void ) { int num; centity_t *cent; playerState_t *ps; // set cg.frameInterpolation if ( cg.nextSnap ) { int delta; delta = (cg.nextSnap->serverTime - cg.snap->serverTime); if ( delta == 0 ) { cg.frameInterpolation = 0; } else { cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; } } else { cg.frameInterpolation = 0; // actually, it should never be used, because // no entities should be marked as interpolating } // the auto-rotating items will all have the same axis cg.autoAngles[0] = 0; cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0; cg.autoAngles[2] = 0; cg.autoAnglesFast[0] = 0; cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; cg.autoAnglesFast[2] = 0; AnglesToAxis( cg.autoAngles, cg.autoAxis ); AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); // generate and add the entity from the playerstate ps = &cg.predictedPlayerState; BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse ); CG_AddCEntity( &cg.predictedPlayerEntity ); // lerp the non-predicted value for lightning gun origins CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] ); //unlagged - early transitioning if ( cg.nextSnap ) { // pre-add some of the entities sent over by the server // we have data for them and they don't need to interpolate for ( num = 0 ; num < cg.nextSnap->numEntities ; num++ ) { cent = &cg_entities[ cg.nextSnap->entities[ num ].number ]; if ( cent->nextState.eType == ET_MISSILE || cent->nextState.eType == ET_GENERAL ) { // transition it immediately and add it CG_TransitionEntity( cent ); cent->interpolate = qtrue; CG_AddCEntity( cent ); } } } //unlagged - early transitioning // add each entity sent over by the server for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { cent = &cg_entities[ cg.snap->entities[ num ].number ]; //unlagged - early transitioning if ( !cg.nextSnap || (cent->nextState.eType != ET_MISSILE && cent->nextState.eType != ET_GENERAL) ) { //unlagged - early transitioning CG_AddCEntity( cent ); } //Also unlagged } } openarena_0.8.8.orig/code/cgame/cg_playerstate.c0000644000175000017500000003601011711076136020365 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_playerstate.c -- this file acts on changes in a new playerState_t // With normal play, this will be done after local prediction, but when // following another player or playing back a demo, it will be checked // when the snapshot transitions like all the other entities #include "cg_local.h" /* ============== CG_CheckAmmo If the ammo has gone low enough to generate the warning, play a sound ============== */ void CG_CheckAmmo( void ) { int i; int total; int previous; int weapons; // see about how many seconds of ammo we have remaining weapons = cg.snap->ps.stats[ STAT_WEAPONS ]; total = 0; for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) { if ( ! ( weapons & ( 1 << i ) ) || i == WP_GRAPPLING_HOOK ) { continue; } switch ( i ) { case WP_ROCKET_LAUNCHER: case WP_GRENADE_LAUNCHER: case WP_RAILGUN: case WP_SHOTGUN: //#ifdef MISSIONPACK case WP_PROX_LAUNCHER: //#endif total += cg.snap->ps.ammo[i] * 1000; break; default: total += cg.snap->ps.ammo[i] * 200; break; } if ( total >= 5000 ) { cg.lowAmmoWarning = 0; return; } } previous = cg.lowAmmoWarning; if ( total == 0 ) { cg.lowAmmoWarning = 2; } else { cg.lowAmmoWarning = 1; } // play a sound on transitions if ( cg.lowAmmoWarning != previous ) { trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); } } /* ============== CG_DamageFeedback ============== */ void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) { float left, front, up; float kick; int health; float scale; vec3_t dir; vec3_t angles; float dist; float yaw, pitch; // show the attacking player's head and name in corner cg.attackerTime = cg.time; // the lower on health you are, the greater the view kick will be health = cg.snap->ps.stats[STAT_HEALTH]; if ( health < 40 ) { scale = 1; } else { scale = 40.0 / health; } kick = damage * scale; if (kick < 5) kick = 5; if (kick > 10) kick = 10; // if yaw and pitch are both 255, make the damage always centered (falling, etc) if ( yawByte == 255 && pitchByte == 255 ) { cg.damageX = 0; cg.damageY = 0; cg.v_dmg_roll = 0; cg.v_dmg_pitch = -kick; } else { // positional pitch = pitchByte / 255.0 * 360; yaw = yawByte / 255.0 * 360; angles[PITCH] = pitch; angles[YAW] = yaw; angles[ROLL] = 0; AngleVectors( angles, dir, NULL, NULL ); VectorSubtract( vec3_origin, dir, dir ); front = DotProduct (dir, cg.refdef.viewaxis[0] ); left = DotProduct (dir, cg.refdef.viewaxis[1] ); up = DotProduct (dir, cg.refdef.viewaxis[2] ); dir[0] = front; dir[1] = left; dir[2] = 0; dist = VectorLength( dir ); if ( dist < 0.1 ) { dist = 0.1f; } cg.v_dmg_roll = kick * left; cg.v_dmg_pitch = -kick * front; if ( front <= 0.1 ) { front = 0.1f; } cg.damageX = -left / front; cg.damageY = up / dist; } // clamp the position if ( cg.damageX > 1.0 ) { cg.damageX = 1.0; } if ( cg.damageX < - 1.0 ) { cg.damageX = -1.0; } if ( cg.damageY > 1.0 ) { cg.damageY = 1.0; } if ( cg.damageY < - 1.0 ) { cg.damageY = -1.0; } // don't let the screen flashes vary as much if ( kick > 10 ) { kick = 10; } cg.damageValue = kick; cg.v_dmg_time = cg.time + DAMAGE_TIME; cg.damageTime = cg.snap->serverTime; } /* ================ CG_Respawn A respawn happened this snapshot ================ */ void CG_Respawn( void ) { // no error decay on player movement cg.thisFrameTeleport = qtrue; // display weapons available cg.weaponSelectTime = cg.time; // select the weapon the server says we are using cg.weaponSelect = cg.snap->ps.weapon; } extern char *eventnames[]; /* ============== CG_CheckPlayerstateEvents ============== */ void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) { int i; int event; centity_t *cent; if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) { cent = &cg_entities[ ps->clientNum ]; cent->currentState.event = ps->externalEvent; cent->currentState.eventParm = ps->externalEventParm; CG_EntityEvent( cent, cent->lerpOrigin ); } cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ]; // go through the predictable events buffer for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { // if we have a new predictable event if ( i >= ops->eventSequence // or the server told us to play another event instead of a predicted event we already issued // or something the server told us changed our prediction causing a different event || (i > ops->eventSequence - MAX_PS_EVENTS && ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]) ) { event = ps->events[ i & (MAX_PS_EVENTS-1) ]; cent->currentState.event = event; cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; CG_EntityEvent( cent, cent->lerpOrigin ); cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; cg.eventSequence++; } } } /* ================== CG_CheckChangedPredictableEvents ================== */ void CG_CheckChangedPredictableEvents( playerState_t *ps ) { int i; int event; centity_t *cent; cent = &cg.predictedPlayerEntity; for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { // if (i >= cg.eventSequence) { continue; } // if this event is not further back in than the maximum predictable events we remember if (i > cg.eventSequence - MAX_PREDICTED_EVENTS) { // if the new playerstate event is different from a previously predicted one if ( ps->events[i & (MAX_PS_EVENTS-1)] != cg.predictableEvents[i & (MAX_PREDICTED_EVENTS-1) ] ) { event = ps->events[ i & (MAX_PS_EVENTS-1) ]; cent->currentState.event = event; cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; CG_EntityEvent( cent, cent->lerpOrigin ); cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; if ( cg_showmiss.integer ) { CG_Printf("WARNING: changed predicted event\n"); } } } } } /* ================== pushReward ================== */ static void pushReward(sfxHandle_t sfx, qhandle_t shader, int rewardCount) { if (cg.rewardStack < (MAX_REWARDSTACK-1)) { cg.rewardStack++; cg.rewardSound[cg.rewardStack] = sfx; cg.rewardShader[cg.rewardStack] = shader; cg.rewardCount[cg.rewardStack] = rewardCount; } } /* ================== CG_CheckLocalSounds ================== */ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) { int highScore, health, armor, reward; sfxHandle_t sfx; // don't play the sounds if the player just changed teams if ( ps->persistant[PERS_TEAM] != ops->persistant[PERS_TEAM] ) { return; } // hit changes if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) { armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff; health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8; #ifdef MISSIONPACK if (armor > 50 ) { trap_S_StartLocalSound( cgs.media.hitSoundHighArmor, CHAN_LOCAL_SOUND ); } else if (armor || health > 100) { trap_S_StartLocalSound( cgs.media.hitSoundLowArmor, CHAN_LOCAL_SOUND ); } else { trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND ); } #else trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND ); #endif } else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) { trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND ); } // health changes of more than -1 should make pain sounds if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) { if ( ps->stats[STAT_HEALTH] > 0 ) { CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] ); } } // if we are going into the intermission, don't start any voices if ( cg.intermissionStarted ) { return; } // reward sounds reward = qfalse; if (ps->persistant[PERS_CAPTURES] != ops->persistant[PERS_CAPTURES]) { pushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]); reward = qtrue; //Com_Printf("capture\n"); } if (ps->persistant[PERS_IMPRESSIVE_COUNT] != ops->persistant[PERS_IMPRESSIVE_COUNT]) { #ifdef MISSIONPACK if (ps->persistant[PERS_IMPRESSIVE_COUNT] == 1) { sfx = cgs.media.firstImpressiveSound; } else { sfx = cgs.media.impressiveSound; } #else sfx = cgs.media.impressiveSound; #endif pushReward(sfx, cgs.media.medalImpressive, ps->persistant[PERS_IMPRESSIVE_COUNT]); reward = qtrue; //Com_Printf("impressive\n"); } //KK-OAX We Just Won't Draw The Excellent Stuff if Multikills are Enabled!!! if( !cgs.altExcellent ) { if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) { #ifdef MISSIONPACK if (ps->persistant[PERS_EXCELLENT_COUNT] == 1) { sfx = cgs.media.firstExcellentSound; } else { sfx = cgs.media.excellentSound; } #else sfx = cgs.media.excellentSound; #endif pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]); reward = qtrue; //Com_Printf("excellent\n"); } } if (ps->persistant[PERS_GAUNTLET_FRAG_COUNT] != ops->persistant[PERS_GAUNTLET_FRAG_COUNT]) { #ifdef MISSIONPACK if (ops->persistant[PERS_GAUNTLET_FRAG_COUNT] == 1) { sfx = cgs.media.firstHumiliationSound; } else { sfx = cgs.media.humiliationSound; } #else sfx = cgs.media.humiliationSound; #endif pushReward(sfx, cgs.media.medalGauntlet, ps->persistant[PERS_GAUNTLET_FRAG_COUNT]); reward = qtrue; //Com_Printf("guantlet frag\n"); } if (ps->persistant[PERS_DEFEND_COUNT] != ops->persistant[PERS_DEFEND_COUNT]) { pushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]); reward = qtrue; //Com_Printf("defend\n"); } if (ps->persistant[PERS_ASSIST_COUNT] != ops->persistant[PERS_ASSIST_COUNT]) { pushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]); reward = qtrue; //Com_Printf("assist\n"); } // if any of the player event bits changed if (ps->persistant[PERS_PLAYEREVENTS] != ops->persistant[PERS_PLAYEREVENTS]) { if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD)) { trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER ); } else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD)) { trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER ); } else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT)) { trap_S_StartLocalSound( cgs.media.holyShitSound, CHAN_ANNOUNCER ); } reward = qtrue; } // check for flag pickup if ( cgs.gametype > GT_TEAM ) { if ((ps->powerups[PW_REDFLAG] != ops->powerups[PW_REDFLAG] && ps->powerups[PW_REDFLAG]) || (ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) || (ps->powerups[PW_NEUTRALFLAG] != ops->powerups[PW_NEUTRALFLAG] && ps->powerups[PW_NEUTRALFLAG]) ) { trap_S_StartLocalSound( cgs.media.youHaveFlagSound, CHAN_ANNOUNCER ); } } // lead changes if (!reward) { // if ( !cg.warmup ) { // never play lead changes during warmup if ( ps->persistant[PERS_RANK] != ops->persistant[PERS_RANK] ) { if ( cgs.gametype < GT_TEAM || cgs.ffa_gt==1) { if ( ps->persistant[PERS_RANK] == 0 ) { CG_AddBufferedSound(cgs.media.takenLeadSound); } else if ( ps->persistant[PERS_RANK] == RANK_TIED_FLAG ) { CG_AddBufferedSound(cgs.media.tiedLeadSound); } else if ( ( ops->persistant[PERS_RANK] & ~RANK_TIED_FLAG ) == 0 ) { CG_AddBufferedSound(cgs.media.lostLeadSound); } } } } } // timelimit warnings if ( cgs.timelimit > 0 && cgs.timelimit <= 1000 ) { int msec; msec = cg.time - cgs.levelStartTime; if ( !( cg.timelimitWarnings & 4 ) && msec > ( cgs.timelimit * 60 + 2 ) * 1000 ) { cg.timelimitWarnings |= 1 | 2 | 4; trap_S_StartLocalSound( cgs.media.suddenDeathSound, CHAN_ANNOUNCER ); } else if ( !( cg.timelimitWarnings & 2 ) && msec > (cgs.timelimit - 1) * 60 * 1000 ) { cg.timelimitWarnings |= 1 | 2; trap_S_StartLocalSound( cgs.media.oneMinuteSound, CHAN_ANNOUNCER ); } else if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && msec > (cgs.timelimit - 5) * 60 * 1000 ) { cg.timelimitWarnings |= 1; trap_S_StartLocalSound( cgs.media.fiveMinuteSound, CHAN_ANNOUNCER ); } } // fraglimit warnings if ( cgs.fraglimit > 0 && cgs.gametype < GT_CTF) { highScore = cgs.scores1; if (cgs.gametype == GT_TEAM && cgs.scores2 > highScore) { highScore = cgs.scores2; } if ( !( cg.fraglimitWarnings & 4 ) && highScore == (cgs.fraglimit - 1) ) { cg.fraglimitWarnings |= 1 | 2 | 4; CG_AddBufferedSound(cgs.media.oneFragSound); } else if ( cgs.fraglimit > 2 && !( cg.fraglimitWarnings & 2 ) && highScore == (cgs.fraglimit - 2) ) { cg.fraglimitWarnings |= 1 | 2; CG_AddBufferedSound(cgs.media.twoFragSound); } else if ( cgs.fraglimit > 3 && !( cg.fraglimitWarnings & 1 ) && highScore == (cgs.fraglimit - 3) ) { cg.fraglimitWarnings |= 1; CG_AddBufferedSound(cgs.media.threeFragSound); } } } /* =============== CG_TransitionPlayerState =============== */ void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) { // check for changing follow mode if ( ps->clientNum != ops->clientNum ) { cg.thisFrameTeleport = qtrue; // make sure we don't get any unwanted transition effects *ops = *ps; } // damage events (player is getting wounded) if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) { CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount ); } // respawning if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) { CG_Respawn(); } if ( cg.mapRestart ) { CG_Respawn(); cg.mapRestart = qfalse; } if ( cg.snap->ps.pm_type != PM_INTERMISSION && ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) { CG_CheckLocalSounds( ps, ops ); } // check for going low on ammo CG_CheckAmmo(); // run events CG_CheckPlayerstateEvents( ps, ops ); // smooth the ducking viewheight change if ( ps->viewheight != ops->viewheight ) { cg.duckChange = ps->viewheight - ops->viewheight; cg.duckTime = cg.time; } } openarena_0.8.8.orig/code/cgame/cg_players.c0000644000175000017500000021340711656310265017520 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_players.c -- handle the media and animation for player entities #include "cg_local.h" char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = { "*death1.wav", "*death2.wav", "*death3.wav", "*jump1.wav", "*pain25_1.wav", "*pain50_1.wav", "*pain75_1.wav", "*pain100_1.wav", "*falling1.wav", "*gasp.wav", "*drown.wav", "*fall1.wav", "*taunt.wav" }; /* ================ CG_CustomSound ================ */ sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ) { clientInfo_t *ci; int i; if ( soundName[0] != '*' ) { return trap_S_RegisterSound( soundName, qfalse ); } if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) { if ( !strcmp( soundName, cg_customSoundNames[i] ) ) { return ci->sounds[i]; } } CG_Error( "Unknown custom sound: %s", soundName ); return 0; } /* ============================================================================= CLIENT INFO ============================================================================= */ /* ====================== CG_ParseAnimationFile Read a configuration file containing animation coutns and rates models/players/visor/animation.cfg, etc ====================== */ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) { char *text_p, *prev; int len; int i; char *token; float fps; int skip; char text[20000]; fileHandle_t f; animation_t *animations; animations = ci->animations; // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { return qfalse; } if ( len >= sizeof( text ) - 1 ) { CG_Printf( "File %s too long\n", filename ); trap_FS_FCloseFile( f ); return qfalse; } trap_FS_Read( text, len, f ); text[len] = 0; trap_FS_FCloseFile( f ); // parse the text text_p = text; skip = 0; // quite the compiler warning ci->footsteps = FOOTSTEP_NORMAL; VectorClear( ci->headOffset ); ci->gender = GENDER_MALE; ci->fixedlegs = qfalse; ci->fixedtorso = qfalse; // read optional parameters while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) { ci->footsteps = FOOTSTEP_NORMAL; } else if ( !Q_stricmp( token, "boot" ) ) { ci->footsteps = FOOTSTEP_BOOT; } else if ( !Q_stricmp( token, "flesh" ) ) { ci->footsteps = FOOTSTEP_FLESH; } else if ( !Q_stricmp( token, "mech" ) ) { ci->footsteps = FOOTSTEP_MECH; } else if ( !Q_stricmp( token, "energy" ) ) { ci->footsteps = FOOTSTEP_ENERGY; } else { CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token ); } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } ci->headOffset[i] = atof( token ); } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } if ( token[0] == 'f' || token[0] == 'F' ) { ci->gender = GENDER_FEMALE; } else if ( token[0] == 'n' || token[0] == 'N' ) { ci->gender = GENDER_NEUTER; } else { ci->gender = GENDER_MALE; } continue; } else if ( !Q_stricmp( token, "fixedlegs" ) ) { ci->fixedlegs = qtrue; continue; } else if ( !Q_stricmp( token, "fixedtorso" ) ) { ci->fixedtorso = qtrue; continue; } // if it is a number, start parsing animations if ( token[0] >= '0' && token[0] <= '9' ) { text_p = prev; // unget the token break; } Com_Printf( "unknown token '%s' is %s\n", token, filename ); } // read information for each frame for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); if ( !*token ) { if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp; animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames; animations[i].numFrames = animations[TORSO_GESTURE].numFrames; animations[i].reversed = qfalse; animations[i].flipflop = qfalse; continue; } break; } animations[i].firstFrame = atoi( token ); // leg only frames are adjusted to not count the upper body only frames if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } if ( i >= LEGS_WALKCR && i0) { return qtrue; } return qfalse; } /* ========================== CG_FindClientModelFile ========================== */ static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) { char *team, *charactersFolder; int i; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { switch ( ci->team ) { case TEAM_BLUE: { team = "blue"; break; } default: { team = "red"; break; } } } else { team = "default"; } charactersFolder = ""; while(1) { for ( i = 0; i < 2; i++ ) { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/sergei/stroggs/lower_lily_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext ); } else { // "models/players/characters/sergei/lower_lily_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext ); } if ( CG_FileExists( filename ) ) { return qtrue; } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/sergei/stroggs/lower_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext ); } else { // "models/players/characters/sergei/lower_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext ); } } else { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/sergei/stroggs/lower_lily.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext ); } else { // "models/players/characters/sergei/lower_lily.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext ); } } if ( CG_FileExists( filename ) ) { return qtrue; } if ( !teamName || !*teamName ) { break; } } // if tried the heads folder first if ( charactersFolder[0] ) { break; } charactersFolder = "characters/"; } return qfalse; } /* ========================== CG_FindClientHeadFile ========================== */ static qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) { char *team, *headsFolder; int i; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { switch ( ci->team ) { case TEAM_BLUE: { team = "blue"; break; } default: { team = "red"; break; } } } else { team = "default"; } if ( headModelName[0] == '*' ) { headsFolder = "heads/"; headModelName++; } else { headsFolder = ""; } while(1) { for ( i = 0; i < 2; i++ ) { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext ); } if ( CG_FileExists( filename ) ) { return qtrue; } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, team, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, team, ext ); } } else { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext ); } } if ( CG_FileExists( filename ) ) { return qtrue; } if ( !teamName || !*teamName ) { break; } } // if tried the heads folder first if ( headsFolder[0] ) { break; } headsFolder = "heads/"; } return qfalse; } /* ========================== CG_RegisterClientSkin ========================== */ static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) { char filename[MAX_QPATH]; /* Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName ); ci->legsSkin = trap_R_RegisterSkin( filename ); if (!ci->legsSkin) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName ); ci->legsSkin = trap_R_RegisterSkin( filename ); if (!ci->legsSkin) { Com_Printf( "Leg skin load failure: %s\n", filename ); } } Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName ); ci->torsoSkin = trap_R_RegisterSkin( filename ); if (!ci->torsoSkin) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName ); ci->torsoSkin = trap_R_RegisterSkin( filename ); if (!ci->torsoSkin) { Com_Printf( "Torso skin load failure: %s\n", filename ); } } */ if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) { ci->legsSkin = trap_R_RegisterSkin( filename ); } if (!ci->legsSkin) { Com_Printf( "Leg skin load failure: %s\n", filename ); } if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "upper", "skin" ) ) { ci->torsoSkin = trap_R_RegisterSkin( filename ); } if (!ci->torsoSkin) { Com_Printf( "Torso skin load failure: %s\n", filename ); } if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headModelName, headSkinName, "head", "skin" ) ) { ci->headSkin = trap_R_RegisterSkin( filename ); } if (!ci->headSkin) { Com_Printf( "Head skin load failure: %s\n", filename ); } // if any skins failed to load if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) { return qfalse; } return qtrue; } /* ========================== CG_RegisterClientModelname ========================== */ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName, const char *teamName ) { char filename[MAX_QPATH*2]; const char *headName; char newTeamName[MAX_QPATH*2]; if ( headModelName[0] == '\0' ) { headName = modelName; } else { headName = headModelName; } Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); ci->legsModel = trap_R_RegisterModel( filename ); if ( !ci->legsModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName ); ci->legsModel = trap_R_RegisterModel( filename ); if ( !ci->legsModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); ci->torsoModel = trap_R_RegisterModel( filename ); if ( !ci->torsoModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName ); ci->torsoModel = trap_R_RegisterModel( filename ); if ( !ci->torsoModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } if( headName[0] == '*' ) { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName ); } ci->headModel = trap_R_RegisterModel( filename ); // if the head model could not be found and we didn't load from the heads folder try to load from there if ( !ci->headModel && headName[0] != '*' ) { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName ); ci->headModel = trap_R_RegisterModel( filename ); } if ( !ci->headModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } // if any skins failed to load, return failure if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) { if ( teamName && *teamName) { Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", teamName, modelName, skinName, headName, headSkinName ); if( ci->team == TEAM_BLUE ) { Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_BLUETEAM_NAME); } else { Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_REDTEAM_NAME); } if ( !CG_RegisterClientSkin( ci, newTeamName, modelName, skinName, headName, headSkinName ) ) { Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", newTeamName, modelName, skinName, headName, headSkinName ); return qfalse; } } else { Com_Printf( "Failed to load skin file: %s : %s, %s : %s\n", modelName, skinName, headName, headSkinName ); return qfalse; } } // load the animations Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); if ( !CG_ParseAnimationFile( filename, ci ) ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName ); if ( !CG_ParseAnimationFile( filename, ci ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } } if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) { ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); } else if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "tga" ) ) { ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); } if ( !ci->modelIcon ) { return qfalse; } return qtrue; } /* ==================== CG_ColorFromString ==================== */ static void CG_ColorFromString( const char *v, vec3_t color ) { int val; VectorClear( color ); val = atoi( v ); if ( val < 1 || val > 7 ) { VectorSet( color, 1, 1, 1 ); return; } if ( val & 1 ) { color[2] = 1.0f; } if ( val & 2 ) { color[1] = 1.0f; } if ( val & 4 ) { color[0] = 1.0f; } } /* =================== CG_LoadClientInfo Load it now, taking the disk hits. This will usually be deferred to a safe time =================== */ static void CG_LoadClientInfo( int clientNum, clientInfo_t *ci ) { const char *dir, *fallback; int i, modelloaded; const char *s; char teamname[MAX_QPATH]; teamname[0] = 0; #ifdef MISSIONPACK if( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { if( ci->team == TEAM_BLUE ) { Q_strncpyz(teamname, cg_blueTeamName.string, sizeof(teamname) ); } else { Q_strncpyz(teamname, cg_redTeamName.string, sizeof(teamname) ); } } if( teamname[0] ) { strcat( teamname, "/" ); } #endif modelloaded = qtrue; if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) { if ( cg_buildScript.integer ) { CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ); } // fall back to default team name if( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { // keep skin name if( ci->team == TEAM_BLUE ) { Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) ); } else { Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) ); } if ( !CG_RegisterClientModelname( ci, DEFAULT_TEAM_MODEL, ci->skinName, DEFAULT_TEAM_HEAD, ci->skinName, teamname ) ) { CG_Error( "DEFAULT_TEAM_MODEL / skin (%s/%s) failed to register", DEFAULT_TEAM_MODEL, ci->skinName ); } } else { if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) { CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL ); } } modelloaded = qfalse; } ci->newAnims = qfalse; if ( ci->torsoModel ) { orientation_t tag; // if the torso model has the "tag_flag" if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) { ci->newAnims = qtrue; } } // sounds dir = ci->modelName; fallback = (cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) ? DEFAULT_TEAM_MODEL : DEFAULT_MODEL; for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) { s = cg_customSoundNames[i]; if ( !s ) { break; } ci->sounds[i] = 0; // if the model didn't load use the sounds of the default model if (modelloaded) { ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1), qfalse ); } if ( !ci->sounds[i] ) { ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1), qfalse ); } } ci->deferred = qfalse; // reset any existing players and bodies, because they might be in bad // frames for this new model for ( i = 0 ; i < MAX_GENTITIES ; i++ ) { if ( cg_entities[i].currentState.clientNum == clientNum && cg_entities[i].currentState.eType == ET_PLAYER ) { CG_ResetPlayerEntity( &cg_entities[i] ); } } } /* ====================== CG_CopyClientInfoModel ====================== */ static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) { VectorCopy( from->headOffset, to->headOffset ); to->footsteps = from->footsteps; to->gender = from->gender; to->legsModel = from->legsModel; to->legsSkin = from->legsSkin; to->torsoModel = from->torsoModel; to->torsoSkin = from->torsoSkin; to->headModel = from->headModel; to->headSkin = from->headSkin; to->modelIcon = from->modelIcon; to->newAnims = from->newAnims; memcpy( to->animations, from->animations, sizeof( to->animations ) ); memcpy( to->sounds, from->sounds, sizeof( to->sounds ) ); } /* ====================== CG_ScanForExistingClientInfo ====================== */ static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) { int i; clientInfo_t *match; for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid ) { continue; } if ( match->deferred ) { continue; } if ( !Q_stricmp( ci->modelName, match->modelName ) && !Q_stricmp( ci->skinName, match->skinName ) && !Q_stricmp( ci->headModelName, match->headModelName ) && !Q_stricmp( ci->headSkinName, match->headSkinName ) && !Q_stricmp( ci->blueTeam, match->blueTeam ) && !Q_stricmp( ci->redTeam, match->redTeam ) && (cgs.gametype < GT_TEAM || cgs.ffa_gt==1 || ci->team == match->team) ) { // this clientinfo is identical, so use it's handles ci->deferred = qfalse; CG_CopyClientInfoModel( match, ci ); return qtrue; } } // nothing matches, so defer the load return qfalse; } /* ====================== CG_SetDeferredClientInfo We aren't going to load it now, so grab some other client's info to use until we have some spare time. ====================== */ static void CG_SetDeferredClientInfo( int clientNum, clientInfo_t *ci ) { int i; clientInfo_t *match; // if someone else is already the same models and skins we // can just load the client info for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid || match->deferred ) { continue; } if ( Q_stricmp( ci->skinName, match->skinName ) || Q_stricmp( ci->modelName, match->modelName ) || // Q_stricmp( ci->headModelName, match->headModelName ) || // Q_stricmp( ci->headSkinName, match->headSkinName ) || (cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1 && ci->team != match->team) ) { continue; } // just load the real info cause it uses the same models and skins CG_LoadClientInfo( clientNum, ci ); return; } // if we are in teamplay, only grab a model if the skin is correct if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid || match->deferred ) { continue; } if ( Q_stricmp( ci->skinName, match->skinName ) || (cgs.gametype >= GT_TEAM && cgs.ffa_gt != 1 && ci->team != match->team) ) { continue; } ci->deferred = qtrue; CG_CopyClientInfoModel( match, ci ); return; } // load the full model, because we don't ever want to show // an improper team skin. This will cause a hitch for the first // player, when the second enters. Combat shouldn't be going on // yet, so it shouldn't matter CG_LoadClientInfo( clientNum, ci ); return; } // find the first valid clientinfo and grab its stuff for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid ) { continue; } ci->deferred = qtrue; CG_CopyClientInfoModel( match, ci ); return; } // we should never get here... CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" ); CG_LoadClientInfo( clientNum, ci ); } /* ====================== CG_NewClientInfo ====================== */ void CG_NewClientInfo( int clientNum ) { clientInfo_t *ci; clientInfo_t newInfo; const char *configstring; const char *v; char *slash; ci = &cgs.clientinfo[clientNum]; configstring = CG_ConfigString( clientNum + CS_PLAYERS ); if ( !configstring[0] ) { memset( ci, 0, sizeof( *ci ) ); return; // player just left } // build into a temp buffer so the defer checks can use // the old value memset( &newInfo, 0, sizeof( newInfo ) ); // isolate the player's name v = Info_ValueForKey(configstring, "n"); Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) ); // colors v = Info_ValueForKey( configstring, "c1" ); CG_ColorFromString( v, newInfo.color1 ); v = Info_ValueForKey( configstring, "c2" ); CG_ColorFromString( v, newInfo.color2 ); // bot skill v = Info_ValueForKey( configstring, "skill" ); newInfo.botSkill = atoi( v ); // handicap v = Info_ValueForKey( configstring, "hc" ); newInfo.handicap = atoi( v ); // wins v = Info_ValueForKey( configstring, "w" ); newInfo.wins = atoi( v ); // losses v = Info_ValueForKey( configstring, "l" ); newInfo.losses = atoi( v ); // team v = Info_ValueForKey( configstring, "t" ); newInfo.team = atoi( v ); // team task v = Info_ValueForKey( configstring, "tt" ); newInfo.teamTask = atoi(v); // team leader v = Info_ValueForKey( configstring, "tl" ); newInfo.teamLeader = atoi(v); v = Info_ValueForKey( configstring, "g_redteam" ); Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME); v = Info_ValueForKey( configstring, "g_blueteam" ); Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME); // model v = Info_ValueForKey( configstring, "model" ); if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches char modelStr[MAX_QPATH]; char *skin; if( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { Q_strncpyz( newInfo.modelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.modelName ) ); Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); } else { trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) ); if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { skin = "default"; } else { *skin++ = 0; } Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) ); Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) ); } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1 ) { // keep skin name slash = strchr( v, '/' ); if ( slash ) { Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); } } } else { Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) ); slash = strchr( newInfo.modelName, '/' ); if ( !slash ) { // modelName didn not include a skin name Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); } else { Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); // truncate modelName *slash = 0; } } // head model v = Info_ValueForKey( configstring, "hmodel" ); if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches char modelStr[MAX_QPATH]; char *skin; if( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { Q_strncpyz( newInfo.headModelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.headModelName ) ); Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); } else { trap_Cvar_VariableStringBuffer( "headmodel", modelStr, sizeof( modelStr ) ); if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { skin = "default"; } else { *skin++ = 0; } Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) ); Q_strncpyz( newInfo.headModelName, modelStr, sizeof( newInfo.headModelName ) ); } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { // keep skin name slash = strchr( v, '/' ); if ( slash ) { Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); } } } else { Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) ); slash = strchr( newInfo.headModelName, '/' ); if ( !slash ) { // modelName didn not include a skin name Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); } else { Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); // truncate modelName *slash = 0; } } // scan for an existing clientinfo that matches this modelname // so we can avoid loading checks if possible if ( !CG_ScanForExistingClientInfo( &newInfo ) ) { qboolean forceDefer; forceDefer = trap_MemoryRemaining() < 4000000; // if we are defering loads, just have it pick the first valid if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) { // keep whatever they had if it won't violate team skins CG_SetDeferredClientInfo( clientNum, &newInfo ); // if we are low on memory, leave them with this model if ( forceDefer ) { CG_Printf( "Memory is low. Using deferred model.\n" ); newInfo.deferred = qfalse; } } else { CG_LoadClientInfo( clientNum, &newInfo ); } } // replace whatever was there with the new one newInfo.infoValid = qtrue; *ci = newInfo; } /* ====================== CG_LoadDeferredPlayers Called each frame when a player is dead and the scoreboard is up so deferred players can be loaded ====================== */ void CG_LoadDeferredPlayers( void ) { int i; clientInfo_t *ci; // scan for a deferred player to load for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) { if ( ci->infoValid && ci->deferred ) { // if we are low on memory, leave it deferred if ( trap_MemoryRemaining() < 4000000 ) { CG_Printf( "Memory is low. Using deferred model.\n" ); ci->deferred = qfalse; continue; } CG_LoadClientInfo( i, ci ); // break; } } } /* ============================================================================= PLAYER ANIMATION ============================================================================= */ /* =============== CG_SetLerpFrameAnimation may include ANIM_TOGGLEBIT =============== */ static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { animation_t *anim; lf->animationNumber = newAnimation; newAnimation &= ~ANIM_TOGGLEBIT; if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) { CG_Error( "Bad animation number: %i", newAnimation ); } anim = &ci->animations[ newAnimation ]; lf->animation = anim; lf->animationTime = lf->frameTime + anim->initialLerp; if ( cg_debugAnim.integer ) { CG_Printf( "Anim: %i\n", newAnimation ); } } /* =============== CG_RunLerpFrame Sets cg.snap, cg.oldFrame, and cg.backlerp cg.time should be between oldFrameTime and frameTime after exit =============== */ static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) { int f, numFrames; animation_t *anim; // debugging tool to get no animations if ( cg_animSpeed.integer == 0 ) { lf->oldFrame = lf->frame = lf->backlerp = 0; return; } // see if the animation sequence is switching if ( newAnimation != lf->animationNumber || !lf->animation ) { CG_SetLerpFrameAnimation( ci, lf, newAnimation ); } // if we have passed the current frame, move it to // oldFrame and calculate a new frame if ( cg.time >= lf->frameTime ) { lf->oldFrame = lf->frame; lf->oldFrameTime = lf->frameTime; // get the next frame based on the animation anim = lf->animation; if ( !anim->frameLerp ) { return; // shouldn't happen } if ( cg.time < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; f *= speedScale; // adjust for haste, etc numFrames = anim->numFrames; if (anim->flipflop) { numFrames *= 2; } if ( f >= numFrames ) { f -= numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { f = numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = cg.time; } } if ( anim->reversed ) { lf->frame = anim->firstFrame + anim->numFrames - 1 - f; } else if (anim->flipflop && f>=anim->numFrames) { lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames); } else { lf->frame = anim->firstFrame + f; } if ( cg.time > lf->frameTime ) { lf->frameTime = cg.time; if ( cg_debugAnim.integer ) { CG_Printf( "Clamp lf->frameTime\n"); } } } if ( lf->frameTime > cg.time + 200 ) { lf->frameTime = cg.time; } if ( lf->oldFrameTime > cg.time ) { lf->oldFrameTime = cg.time; } // calculate current lerp value if ( lf->frameTime == lf->oldFrameTime ) { lf->backlerp = 0; } else { lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); } } /* =============== CG_ClearLerpFrame =============== */ static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) { lf->frameTime = lf->oldFrameTime = cg.time; CG_SetLerpFrameAnimation( ci, lf, animationNumber ); lf->oldFrame = lf->frame = lf->animation->firstFrame; } /* =============== CG_PlayerAnimation =============== */ static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp, int *torsoOld, int *torso, float *torsoBackLerp ) { clientInfo_t *ci; int clientNum; float speedScale; clientNum = cent->currentState.clientNum; if ( cg_noPlayerAnims.integer ) { *legsOld = *legs = *torsoOld = *torso = 0; return; } if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) { speedScale = 1.5; } else { speedScale = 1; } ci = &cgs.clientinfo[ clientNum ]; // do the shuffle turn frames locally if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { CG_RunLerpFrame( ci, ¢->pe.legs, LEGS_TURN, speedScale ); } else { CG_RunLerpFrame( ci, ¢->pe.legs, cent->currentState.legsAnim, speedScale ); } *legsOld = cent->pe.legs.oldFrame; *legs = cent->pe.legs.frame; *legsBackLerp = cent->pe.legs.backlerp; CG_RunLerpFrame( ci, ¢->pe.torso, cent->currentState.torsoAnim, speedScale ); *torsoOld = cent->pe.torso.oldFrame; *torso = cent->pe.torso.frame; *torsoBackLerp = cent->pe.torso.backlerp; } /* ============================================================================= PLAYER ANGLES ============================================================================= */ /* ================== CG_SwingAngles ================== */ static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qboolean *swinging ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = cg.frametime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = cg.frametime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } } /* ================= CG_AddPainTwitch ================= */ static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) { int t; float f; t = cg.time - cent->pe.painTime; if ( t >= PAIN_TWITCH_TIME ) { return; } f = 1.0 - (float)t / PAIN_TWITCH_TIME; if ( cent->pe.painDirection ) { torsoAngles[ROLL] += 20 * f; } else { torsoAngles[ROLL] -= 20 * f; } } /* =============== CG_PlayerAngles Handles seperate torso motion legs pivot based on direction of movement head always looks exactly at cent->lerpAngles if motion < 20 degrees, show in head only if < 45 degrees, also show in torso =============== */ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { vec3_t legsAngles, torsoAngles, headAngles; float dest; static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 }; vec3_t velocity; float speed; int dir, clientNum; clientInfo_t *ci; VectorCopy( cent->lerpAngles, headAngles ); headAngles[YAW] = AngleMod( headAngles[YAW] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ((cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT) != TORSO_STAND && (cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT) != TORSO_STAND2)) { // if not standing still, always point all in the same direction cent->pe.torso.yawing = qtrue; // always center cent->pe.torso.pitching = qtrue; // always center cent->pe.legs.yawing = qtrue; // always center } // adjust legs for movement dir if ( cent->currentState.eFlags & EF_DEAD ) { // don't let dead bodies twitch dir = 0; } else { dir = cent->currentState.angles2[YAW]; if ( dir < 0 || dir > 7 ) { CG_Error( "Bad player movement angle" ); } } legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ]; torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ]; // torso CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing ); CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); torsoAngles[YAW] = cent->pe.torso.yawAngle; legsAngles[YAW] = cent->pe.legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[PITCH] > 180 ) { dest = (-360 + headAngles[PITCH]) * 0.75f; } else { dest = headAngles[PITCH] * 0.75f; } CG_SwingAngles( dest, 15, 30, 0.1f, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching ); torsoAngles[PITCH] = cent->pe.torso.pitchAngle; // clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; if ( ci->fixedtorso ) { torsoAngles[PITCH] = 0.0f; } } // --------- roll ------------- // lean towards the direction of travel VectorCopy( cent->currentState.pos.trDelta, velocity ); speed = VectorNormalize( velocity ); if ( speed ) { vec3_t axis[3]; float side; speed *= 0.05f; AnglesToAxis( legsAngles, axis ); side = speed * DotProduct( velocity, axis[1] ); legsAngles[ROLL] -= side; side = speed * DotProduct( velocity, axis[0] ); legsAngles[PITCH] += side; } // clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; if ( ci->fixedlegs ) { legsAngles[YAW] = torsoAngles[YAW]; legsAngles[PITCH] = 0.0f; legsAngles[ROLL] = 0.0f; } } // pain twitch CG_AddPainTwitch( cent, torsoAngles ); // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesToAxis( legsAngles, legs ); AnglesToAxis( torsoAngles, torso ); AnglesToAxis( headAngles, head ); } //========================================================================== /* =============== CG_HasteTrail =============== */ static void CG_HasteTrail( centity_t *cent ) { localEntity_t *smoke; vec3_t origin; int anim; if ( cent->trailTime > cg.time ) { return; } anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT; if ( anim != LEGS_RUN && anim != LEGS_BACK ) { return; } cent->trailTime += 100; if ( cent->trailTime < cg.time ) { cent->trailTime = cg.time; } VectorCopy( cent->lerpOrigin, origin ); origin[2] -= 16; smoke = CG_SmokePuff( origin, vec3_origin, 8, 1, 1, 1, 1, 500, cg.time, 0, 0, cgs.media.hastePuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } /* =============== CG_BreathPuffs =============== */ static void CG_BreathPuffs( centity_t *cent, refEntity_t *head) { clientInfo_t *ci; vec3_t up, origin; int contents; ci = &cgs.clientinfo[ cent->currentState.number ]; if (!cg_enableBreath.integer) { return; } if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson) { return; } if ( cent->currentState.eFlags & EF_DEAD ) { return; } contents = CG_PointContents( head->origin, 0 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { return; } if ( ci->breathPuffTime > cg.time ) { return; } VectorSet( up, 0, 0, 8 ); VectorMA(head->origin, 8, head->axis[0], origin); VectorMA(origin, -4, head->axis[2], origin); CG_SmokePuff( origin, up, 16, 1, 1, 1, 0.66f, 1500, cg.time, cg.time + 400, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); ci->breathPuffTime = cg.time + 2000; } /* =============== CG_DustTrail =============== */ static void CG_DustTrail( centity_t *cent ) { int anim; localEntity_t *dust; vec3_t end, vel; trace_t tr; if (!cg_enableDust.integer) return; if ( cent->dustTrailTime > cg.time ) { return; } anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT; if ( anim != LEGS_LANDB && anim != LEGS_LAND ) { return; } cent->dustTrailTime += 40; if ( cent->dustTrailTime < cg.time ) { cent->dustTrailTime = cg.time; } VectorCopy(cent->currentState.pos.trBase, end); end[2] -= 64; CG_Trace( &tr, cent->currentState.pos.trBase, NULL, NULL, end, cent->currentState.number, MASK_PLAYERSOLID ); if ( !(tr.surfaceFlags & SURF_DUST) ) return; VectorCopy( cent->currentState.pos.trBase, end ); end[2] -= 16; VectorSet(vel, 0, 0, -30); dust = CG_SmokePuff( end, vel, 24, .8f, .8f, 0.7f, 0.33f, 500, cg.time, 0, 0, cgs.media.dustPuffShader ); } /* =============== CG_TrailItem =============== */ static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) { refEntity_t ent; vec3_t angles; vec3_t axis[3]; VectorCopy( cent->lerpAngles, angles ); angles[PITCH] = 0; angles[ROLL] = 0; AnglesToAxis( angles, axis ); memset( &ent, 0, sizeof( ent ) ); VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin ); ent.origin[2] += 16; angles[YAW] += 90; AnglesToAxis( angles, ent.axis ); ent.hModel = hModel; trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_PlayerFlag =============== */ static void CG_PlayerFlag( centity_t *cent, qhandle_t hSkin, refEntity_t *torso ) { clientInfo_t *ci; refEntity_t pole; refEntity_t flag; vec3_t angles, dir; int legsAnim, flagAnim, updateangles; float angle, d; // show the flag pole model memset( &pole, 0, sizeof(pole) ); pole.hModel = cgs.media.flagPoleModel; VectorCopy( torso->lightingOrigin, pole.lightingOrigin ); pole.shadowPlane = torso->shadowPlane; pole.renderfx = torso->renderfx; CG_PositionEntityOnTag( &pole, torso, torso->hModel, "tag_flag" ); trap_R_AddRefEntityToScene( &pole ); // show the flag model memset( &flag, 0, sizeof(flag) ); flag.hModel = cgs.media.flagFlapModel; flag.customSkin = hSkin; VectorCopy( torso->lightingOrigin, flag.lightingOrigin ); flag.shadowPlane = torso->shadowPlane; flag.renderfx = torso->renderfx; VectorClear(angles); updateangles = qfalse; legsAnim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if( legsAnim == LEGS_IDLE || legsAnim == LEGS_IDLECR ) { flagAnim = FLAG_STAND; } else if ( legsAnim == LEGS_WALK || legsAnim == LEGS_WALKCR ) { flagAnim = FLAG_STAND; updateangles = qtrue; } else { flagAnim = FLAG_RUN; updateangles = qtrue; } if ( updateangles ) { VectorCopy( cent->currentState.pos.trDelta, dir ); // add gravity dir[2] += 100; VectorNormalize( dir ); d = DotProduct(pole.axis[2], dir); // if there is anough movement orthogonal to the flag pole if (fabs(d) < 0.9) { // d = DotProduct(pole.axis[0], dir); if (d > 1.0f) { d = 1.0f; } else if (d < -1.0f) { d = -1.0f; } angle = acos(d); d = DotProduct(pole.axis[1], dir); if (d < 0) { angles[YAW] = 360 - angle * 180 / M_PI; } else { angles[YAW] = angle * 180 / M_PI; } if (angles[YAW] < 0) angles[YAW] += 360; if (angles[YAW] > 360) angles[YAW] -= 360; //vectoangles( cent->currentState.pos.trDelta, tmpangles ); //angles[YAW] = tmpangles[YAW] + 45 - cent->pe.torso.yawAngle; // change the yaw angle CG_SwingAngles( angles[YAW], 25, 90, 0.15f, ¢->pe.flag.yawAngle, ¢->pe.flag.yawing ); } /* d = DotProduct(pole.axis[2], dir); angle = Q_acos(d); d = DotProduct(pole.axis[1], dir); if (d < 0) { angle = 360 - angle * 180 / M_PI; } else { angle = angle * 180 / M_PI; } if (angle > 340 && angle < 20) { flagAnim = FLAG_RUNUP; } if (angle > 160 && angle < 200) { flagAnim = FLAG_RUNDOWN; } */ } // set the yaw angle angles[YAW] = cent->pe.flag.yawAngle; // lerp the flag animation frames ci = &cgs.clientinfo[ cent->currentState.clientNum ]; CG_RunLerpFrame( ci, ¢->pe.flag, flagAnim, 1 ); flag.oldframe = cent->pe.flag.oldFrame; flag.frame = cent->pe.flag.frame; flag.backlerp = cent->pe.flag.backlerp; AnglesToAxis( angles, flag.axis ); CG_PositionRotatedEntityOnTag( &flag, &pole, pole.hModel, "tag_flag" ); trap_R_AddRefEntityToScene( &flag ); } /* =============== CG_PlayerTokens =============== */ static void CG_PlayerTokens( centity_t *cent, int renderfx ) { int tokens, i, j; float angle; refEntity_t ent; vec3_t dir, origin; skulltrail_t *trail; trail = &cg.skulltrails[cent->currentState.number]; tokens = cent->currentState.generic1; if ( !tokens ) { trail->numpositions = 0; return; } if ( tokens > MAX_SKULLTRAIL ) { tokens = MAX_SKULLTRAIL; } // add skulls if there are more than last time for (i = 0; i < tokens - trail->numpositions; i++) { for (j = trail->numpositions; j > 0; j--) { VectorCopy(trail->positions[j-1], trail->positions[j]); } VectorCopy(cent->lerpOrigin, trail->positions[0]); } trail->numpositions = tokens; // move all the skulls along the trail VectorCopy(cent->lerpOrigin, origin); for (i = 0; i < trail->numpositions; i++) { VectorSubtract(trail->positions[i], origin, dir); if (VectorNormalize(dir) > 30) { VectorMA(origin, 30, dir, trail->positions[i]); } VectorCopy(trail->positions[i], origin); } memset( &ent, 0, sizeof( ent ) ); if( cgs.clientinfo[ cent->currentState.clientNum ].team == TEAM_BLUE ) { ent.hModel = cgs.media.redCubeModel; } else { ent.hModel = cgs.media.blueCubeModel; } ent.renderfx = renderfx; VectorCopy(cent->lerpOrigin, origin); for (i = 0; i < trail->numpositions; i++) { VectorSubtract(origin, trail->positions[i], ent.axis[0]); ent.axis[0][2] = 0; VectorNormalize(ent.axis[0]); VectorSet(ent.axis[2], 0, 0, 1); CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]); VectorCopy(trail->positions[i], ent.origin); angle = (((cg.time + 500 * MAX_SKULLTRAIL - 500 * i) / 16) & 255) * (M_PI * 2) / 255; ent.origin[2] += sin(angle) * 10; trap_R_AddRefEntityToScene( &ent ); VectorCopy(trail->positions[i], origin); } } /* =============== CG_PlayerPowerups =============== */ static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) { int powerups; clientInfo_t *ci; powerups = cent->currentState.powerups; if ( !powerups ) { return; } // quad gives a dlight if ( powerups & ( 1 << PW_QUAD ) ) { trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 ); } // flight plays a looped sound if ( powerups & ( 1 << PW_FLIGHT ) ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound ); } ci = &cgs.clientinfo[ cent->currentState.clientNum ]; // redflag if ( powerups & ( 1 << PW_REDFLAG ) ) { if (ci->newAnims) { CG_PlayerFlag( cent, cgs.media.redFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.redFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f ); } // blueflag if ( powerups & ( 1 << PW_BLUEFLAG ) ) { if (ci->newAnims){ CG_PlayerFlag( cent, cgs.media.blueFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.blueFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 ); } // neutralflag if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) { if (ci->newAnims) { CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.neutralFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 ); } // haste leaves smoke trails if ( powerups & ( 1 << PW_HASTE ) ) { CG_HasteTrail( cent ); } } /* =============== CG_PlayerFloatSprite Float a sprite over the player's head =============== */ static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) { int rf; refEntity_t ent; if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { rf = RF_THIRD_PERSON; // only show in mirrors } else { rf = 0; } memset( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); ent.origin[2] += 48; ent.reType = RT_SPRITE; ent.customShader = shader; ent.radius = 10; ent.renderfx = rf; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 255; trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_PlayerSprites Float sprites over the player's head =============== */ static void CG_PlayerSprites( centity_t *cent ) { int team; if ( cent->currentState.eFlags & EF_CONNECTION ) { CG_PlayerFloatSprite( cent, cgs.media.connectionShader ); return; } if ( cent->currentState.eFlags & EF_TALK ) { CG_PlayerFloatSprite( cent, cgs.media.balloonShader ); return; } if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) { CG_PlayerFloatSprite( cent, cgs.media.medalImpressive ); return; } if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) { CG_PlayerFloatSprite( cent, cgs.media.medalExcellent ); return; } if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) { CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet ); return; } if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) { CG_PlayerFloatSprite( cent, cgs.media.medalDefend ); return; } if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) { CG_PlayerFloatSprite( cent, cgs.media.medalAssist ); return; } if ( cent->currentState.eFlags & EF_AWARD_CAP ) { CG_PlayerFloatSprite( cent, cgs.media.medalCapture ); return; } team = cgs.clientinfo[ cent->currentState.clientNum ].team; if ( !(cent->currentState.eFlags & EF_DEAD) && cg.snap->ps.persistant[PERS_TEAM] == team && cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { if (cg_drawFriend.integer) { CG_PlayerFloatSprite( cent, cgs.media.friendShader ); } return; } } /* =============== CG_PlayerShadow Returns the Z component of the surface being shadowed should it return a full plane instead of a Z? =============== */ #define SHADOW_DISTANCE 128 static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) { vec3_t end, mins = {-15, -15, 0}, maxs = {15, 15, 2}; trace_t trace; float alpha; *shadowPlane = 0; if ( cg_shadows.integer == 0 ) { return qfalse; } // no shadows when invisible if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) { return qfalse; } // send a trace down from the player to the ground VectorCopy( cent->lerpOrigin, end ); end[2] -= SHADOW_DISTANCE; trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID ); // no shadow if too high if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) { return qfalse; } *shadowPlane = trace.endpos[2] + 1; if ( cg_shadows.integer != 1 ) { // no mark for stencil or projection shadows return qtrue; } // fade the shadow out with height alpha = 1.0 - trace.fraction; // bk0101022 - hack / FPE - bogus planes? //assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) // add the mark as a temporary, so it goes directly to the renderer // without taking a spot in the cg_marks array CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue ); return qtrue; } /* =============== CG_PlayerSplash Draw a mark at the water surface =============== */ static void CG_PlayerSplash( centity_t *cent ) { vec3_t start, end; trace_t trace; int contents; polyVert_t verts[4]; if ( !cg_shadows.integer ) { return; } VectorCopy( cent->lerpOrigin, end ); end[2] -= 24; // if the feet aren't in liquid, don't make a mark // this won't handle moving water brushes, but they wouldn't draw right anyway... contents = CG_PointContents( end, 0 ); if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { return; } VectorCopy( cent->lerpOrigin, start ); start[2] += 32; // if the head isn't out of liquid, don't make a mark contents = CG_PointContents( start, 0 ); if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { return; } // trace down to find the surface trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ); if ( trace.fraction == 1.0 ) { return; } // create a mark polygon VectorCopy( trace.endpos, verts[0].xyz ); verts[0].xyz[0] -= 32; verts[0].xyz[1] -= 32; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy( trace.endpos, verts[1].xyz ); verts[1].xyz[0] -= 32; verts[1].xyz[1] += 32; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy( trace.endpos, verts[2].xyz ); verts[2].xyz[0] += 32; verts[2].xyz[1] += 32; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy( trace.endpos, verts[3].xyz ); verts[3].xyz[0] += 32; verts[3].xyz[1] -= 32; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts ); } /* =============== CG_AddRefEntityWithPowerups Adds a piece with modifications or duplications for powerups Also called by CG_Missile for quad rockets, but nobody can tell... =============== */ void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team, qboolean isMissile ) { if ( state->powerups & ( 1 << PW_INVIS ) ) { if( (cgs.dmflags & DF_INVIS) == 0) { ent->customShader = cgs.media.invisShader; trap_R_AddRefEntityToScene( ent ); } } else { /* if ( state->eFlags & EF_KAMIKAZE ) { if (team == TEAM_BLUE) ent->customShader = cgs.media.blueKamikazeShader; else ent->customShader = cgs.media.redKamikazeShader; trap_R_AddRefEntityToScene( ent ); } else {*/ trap_R_AddRefEntityToScene( ent ); //} if(!isMissile && (cgs.dmflags & DF_PLAYER_OVERLAY) && !(state->eFlags & EF_DEAD) ) { switch(team) { case TEAM_RED: ent->customShader = cgs.media.redOverlay; trap_R_AddRefEntityToScene( ent ); break; case TEAM_BLUE: ent->customShader = cgs.media.blueOverlay; trap_R_AddRefEntityToScene( ent ); break; default: ent->customShader = cgs.media.neutralOverlay; trap_R_AddRefEntityToScene( ent ); } } if ( state->powerups & ( 1 << PW_QUAD ) ) { if (team == TEAM_RED) ent->customShader = cgs.media.redQuadShader; else ent->customShader = cgs.media.quadShader; trap_R_AddRefEntityToScene( ent ); } if ( state->powerups & ( 1 << PW_REGEN ) ) { if ( ( ( cg.time / 100 ) % 10 ) == 1 ) { ent->customShader = cgs.media.regenShader; trap_R_AddRefEntityToScene( ent ); } } if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) { ent->customShader = cgs.media.battleSuitShader; trap_R_AddRefEntityToScene( ent ); } } } /* ================= CG_LightVerts ================= */ int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts ) { int i, j; float incoming; vec3_t ambientLight; vec3_t lightDir; vec3_t directedLight; trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir ); for (i = 0; i < numVerts; i++) { incoming = DotProduct (normal, lightDir); if ( incoming <= 0 ) { verts[i].modulate[0] = ambientLight[0]; verts[i].modulate[1] = ambientLight[1]; verts[i].modulate[2] = ambientLight[2]; verts[i].modulate[3] = 255; continue; } j = ( ambientLight[0] + incoming * directedLight[0] ); if ( j > 255 ) { j = 255; } verts[i].modulate[0] = j; j = ( ambientLight[1] + incoming * directedLight[1] ); if ( j > 255 ) { j = 255; } verts[i].modulate[1] = j; j = ( ambientLight[2] + incoming * directedLight[2] ); if ( j > 255 ) { j = 255; } verts[i].modulate[2] = j; verts[i].modulate[3] = 255; } return qtrue; } /* =============== CG_Player =============== */ void CG_Player( centity_t *cent ) { clientInfo_t *ci; refEntity_t legs; refEntity_t torso; refEntity_t head; int clientNum; int renderfx; qboolean shadow; float shadowPlane; refEntity_t skull; refEntity_t powerup; int t; float c; float angle; vec3_t dir, angles; // the client number is stored in clientNum. It can't be derived // from the entity number, because a single client may have // multiple corpses on the level using the same clientinfo clientNum = cent->currentState.clientNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { CG_Error( "Bad clientNum on player entity"); } ci = &cgs.clientinfo[ clientNum ]; // it is possible to see corpses from disconnected players that may // not have valid clientinfo if ( !ci->infoValid ) { return; } // get the player model information renderfx = 0; if ( cent->currentState.number == cg.snap->ps.clientNum) { if (!cg.renderingThirdPerson) { renderfx = RF_THIRD_PERSON; // only draw in mirrors } else { if (cg_cameraMode.integer) { return; } } } memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); // get the rotation information CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); // add the talk baloon or disconnect icon CG_PlayerSprites( cent ); // add the shadow shadow = CG_PlayerShadow( cent, &shadowPlane ); // add a water splash if partially in and out of water CG_PlayerSplash( cent ); if ( cg_shadows.integer == 3 && shadow ) { renderfx |= RF_SHADOW_PLANE; } renderfx |= RF_LIGHTING_ORIGIN; // use the same origin for all if( cgs.gametype == GT_HARVESTER ) { CG_PlayerTokens( cent, renderfx ); } // // add the legs // legs.hModel = ci->legsModel; legs.customSkin = ci->legsSkin; VectorCopy( cent->lerpOrigin, legs.origin ); VectorCopy( cent->lerpOrigin, legs.lightingOrigin ); legs.shadowPlane = shadowPlane; legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); // don't positionally lerp at all CG_AddRefEntityWithPowerups( &legs, ¢->currentState, ci->team, qfalse ); // if the model failed, allow the default nullmodel to be displayed if (!legs.hModel) { return; } // // add the torso // torso.hModel = ci->torsoModel; if (!torso.hModel) { return; } torso.customSkin = ci->torsoSkin; VectorCopy( cent->lerpOrigin, torso.lightingOrigin ); CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso"); torso.shadowPlane = shadowPlane; torso.renderfx = renderfx; CG_AddRefEntityWithPowerups( &torso, ¢->currentState, ci->team, qfalse ); if ( cent->currentState.eFlags & EF_KAMIKAZE ) { memset( &skull, 0, sizeof(skull) ); VectorCopy( cent->lerpOrigin, skull.lightingOrigin ); skull.shadowPlane = shadowPlane; skull.renderfx = renderfx; if ( cent->currentState.eFlags & EF_DEAD ) { // one skull bobbing above the dead body angle = ((cg.time / 7) & 255) * (M_PI * 2) / 255; if (angle > M_PI * 2) angle -= (float)M_PI * 2; dir[0] = sin(angle) * 20; dir[1] = cos(angle) * 20; angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255; dir[2] = 15 + sin(angle) * 8; VectorAdd(torso.origin, dir, skull.origin); dir[2] = 0; VectorCopy(dir, skull.axis[1]); VectorNormalize(skull.axis[1]); VectorSet(skull.axis[2], 0, 0, 1); CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); skull.hModel = cgs.media.kamikazeHeadModel; trap_R_AddRefEntityToScene( &skull ); skull.hModel = cgs.media.kamikazeHeadTrail; trap_R_AddRefEntityToScene( &skull ); } else { // three skulls spinning around the player angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255; dir[0] = cos(angle) * 20; dir[1] = sin(angle) * 20; dir[2] = cos(angle) * 20; VectorAdd(torso.origin, dir, skull.origin); angles[0] = sin(angle) * 30; angles[1] = (angle * 180 / M_PI) + 90; if (angles[1] > 360) angles[1] -= 360; angles[2] = 0; AnglesToAxis( angles, skull.axis ); /* dir[2] = 0; VectorInverse(dir); VectorCopy(dir, skull.axis[1]); VectorNormalize(skull.axis[1]); VectorSet(skull.axis[2], 0, 0, 1); CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); */ skull.hModel = cgs.media.kamikazeHeadModel; trap_R_AddRefEntityToScene( &skull ); // flip the trail because this skull is spinning in the other direction VectorInverse(skull.axis[1]); skull.hModel = cgs.media.kamikazeHeadTrail; trap_R_AddRefEntityToScene( &skull ); angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255 + M_PI; if (angle > M_PI * 2) angle -= (float)M_PI * 2; dir[0] = sin(angle) * 20; dir[1] = cos(angle) * 20; dir[2] = cos(angle) * 20; VectorAdd(torso.origin, dir, skull.origin); angles[0] = cos(angle - 0.5 * M_PI) * 30; angles[1] = 360 - (angle * 180 / M_PI); if (angles[1] > 360) angles[1] -= 360; angles[2] = 0; AnglesToAxis( angles, skull.axis ); /* dir[2] = 0; VectorCopy(dir, skull.axis[1]); VectorNormalize(skull.axis[1]); VectorSet(skull.axis[2], 0, 0, 1); CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); */ skull.hModel = cgs.media.kamikazeHeadModel; trap_R_AddRefEntityToScene( &skull ); skull.hModel = cgs.media.kamikazeHeadTrail; trap_R_AddRefEntityToScene( &skull ); angle = ((cg.time / 3) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI; if (angle > M_PI * 2) angle -= (float)M_PI * 2; dir[0] = sin(angle) * 20; dir[1] = cos(angle) * 20; dir[2] = 0; VectorAdd(torso.origin, dir, skull.origin); VectorCopy(dir, skull.axis[1]); VectorNormalize(skull.axis[1]); VectorSet(skull.axis[2], 0, 0, 1); CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]); skull.hModel = cgs.media.kamikazeHeadModel; trap_R_AddRefEntityToScene( &skull ); skull.hModel = cgs.media.kamikazeHeadTrail; trap_R_AddRefEntityToScene( &skull ); } } if ( cent->currentState.powerups & ( 1 << PW_GUARD ) ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.guardPowerupModel; powerup.frame = 0; powerup.oldframe = 0; powerup.customSkin = 0; trap_R_AddRefEntityToScene( &powerup ); } if ( cent->currentState.powerups & ( 1 << PW_SCOUT ) ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.scoutPowerupModel; powerup.frame = 0; powerup.oldframe = 0; powerup.customSkin = 0; trap_R_AddRefEntityToScene( &powerup ); } if ( cent->currentState.powerups & ( 1 << PW_DOUBLER ) ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.doublerPowerupModel; powerup.frame = 0; powerup.oldframe = 0; powerup.customSkin = 0; trap_R_AddRefEntityToScene( &powerup ); } if ( cent->currentState.powerups & ( 1 << PW_AMMOREGEN ) ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.ammoRegenPowerupModel; powerup.frame = 0; powerup.oldframe = 0; powerup.customSkin = 0; trap_R_AddRefEntityToScene( &powerup ); } if ( cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) { if ( !ci->invulnerabilityStartTime ) { ci->invulnerabilityStartTime = cg.time; } ci->invulnerabilityStopTime = cg.time; } else { ci->invulnerabilityStartTime = 0; } if ( (cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) || cg.time - ci->invulnerabilityStopTime < 250 ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.invulnerabilityPowerupModel; powerup.customSkin = 0; // always draw powerup.renderfx &= ~RF_THIRD_PERSON; VectorCopy(cent->lerpOrigin, powerup.origin); if ( cg.time - ci->invulnerabilityStartTime < 250 ) { c = (float) (cg.time - ci->invulnerabilityStartTime) / 250; } else if (cg.time - ci->invulnerabilityStopTime < 250 ) { c = (float) (250 - (cg.time - ci->invulnerabilityStopTime)) / 250; } else { c = 1; } VectorSet( powerup.axis[0], c, 0, 0 ); VectorSet( powerup.axis[1], 0, c, 0 ); VectorSet( powerup.axis[2], 0, 0, c ); trap_R_AddRefEntityToScene( &powerup ); } t = cg.time - ci->medkitUsageTime; if ( ci->medkitUsageTime && t < 500 ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.medkitUsageModel; powerup.customSkin = 0; // always draw powerup.renderfx &= ~RF_THIRD_PERSON; VectorClear(angles); AnglesToAxis(angles, powerup.axis); VectorCopy(cent->lerpOrigin, powerup.origin); powerup.origin[2] += -24 + (float) t * 80 / 500; if ( t > 400 ) { c = (float) (t - 1000) * 0xff / 100; powerup.shaderRGBA[0] = 0xff - c; powerup.shaderRGBA[1] = 0xff - c; powerup.shaderRGBA[2] = 0xff - c; powerup.shaderRGBA[3] = 0xff - c; } else { powerup.shaderRGBA[0] = 0xff; powerup.shaderRGBA[1] = 0xff; powerup.shaderRGBA[2] = 0xff; powerup.shaderRGBA[3] = 0xff; } trap_R_AddRefEntityToScene( &powerup ); } // // add the head // head.hModel = ci->headModel; if (!head.hModel) { return; } head.customSkin = ci->headSkin; VectorCopy( cent->lerpOrigin, head.lightingOrigin ); CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head"); head.shadowPlane = shadowPlane; head.renderfx = renderfx; CG_AddRefEntityWithPowerups( &head, ¢->currentState, ci->team, qfalse ); CG_BreathPuffs(cent, &head); CG_DustTrail(cent); // // add the gun / barrel / flash // CG_AddPlayerWeapon( &torso, NULL, cent, ci->team ); // add powerups floating behind the player CG_PlayerPowerups( cent, &torso ); } //===================================================================== /* =============== CG_ResetPlayerEntity A player just came into view or teleported, so reset all animation info =============== */ void CG_ResetPlayerEntity( centity_t *cent ) { cent->errorTime = -99999; // guarantee no error decay added cent->extrapolated = qfalse; CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.legs, cent->currentState.legsAnim ); CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.torso, cent->currentState.torsoAnim ); BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); VectorCopy( cent->lerpOrigin, cent->rawOrigin ); VectorCopy( cent->lerpAngles, cent->rawAngles ); memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) ); cent->pe.legs.yawAngle = cent->rawAngles[YAW]; cent->pe.legs.yawing = qfalse; cent->pe.legs.pitchAngle = 0; cent->pe.legs.pitching = qfalse; memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) ); cent->pe.torso.yawAngle = cent->rawAngles[YAW]; cent->pe.torso.yawing = qfalse; cent->pe.torso.pitchAngle = cent->rawAngles[PITCH]; cent->pe.torso.pitching = qfalse; if ( cg_debugPosition.integer ) { CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle ); } } openarena_0.8.8.orig/code/cgame/cg_marks.c0000644000175000017500000014757411656310265017171 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_marks.c -- wall marks #include "cg_local.h" /* =================================================================== MARK POLYS =================================================================== */ markPoly_t cg_activeMarkPolys; // double linked list markPoly_t *cg_freeMarkPolys; // single linked list markPoly_t cg_markPolys[MAX_MARK_POLYS]; static int markTotal; /* =================== CG_InitMarkPolys This is called at startup and for tournement restarts =================== */ void CG_InitMarkPolys( void ) { int i; memset( cg_markPolys, 0, sizeof(cg_markPolys) ); cg_activeMarkPolys.nextMark = &cg_activeMarkPolys; cg_activeMarkPolys.prevMark = &cg_activeMarkPolys; cg_freeMarkPolys = cg_markPolys; for ( i = 0 ; i < MAX_MARK_POLYS - 1 ; i++ ) { cg_markPolys[i].nextMark = &cg_markPolys[i+1]; } } /* ================== CG_FreeMarkPoly ================== */ void CG_FreeMarkPoly( markPoly_t *le ) { if ( !le->prevMark ) { CG_Error( "CG_FreeLocalEntity: not active" ); } // remove from the doubly linked active list le->prevMark->nextMark = le->nextMark; le->nextMark->prevMark = le->prevMark; // the free list is only singly linked le->nextMark = cg_freeMarkPolys; cg_freeMarkPolys = le; } /* =================== CG_AllocMark Will allways succeed, even if it requires freeing an old active mark =================== */ markPoly_t *CG_AllocMark( void ) { markPoly_t *le; int time; if ( !cg_freeMarkPolys ) { // no free entities, so free the one at the end of the chain // remove the oldest active entity time = cg_activeMarkPolys.prevMark->time; while (cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time) { CG_FreeMarkPoly( cg_activeMarkPolys.prevMark ); } } le = cg_freeMarkPolys; cg_freeMarkPolys = cg_freeMarkPolys->nextMark; memset( le, 0, sizeof( *le ) ); // link into the active list le->nextMark = cg_activeMarkPolys.nextMark; le->prevMark = &cg_activeMarkPolys; cg_activeMarkPolys.nextMark->prevMark = le; cg_activeMarkPolys.nextMark = le; return le; } /* ================= CG_ImpactMark origin should be a point within a unit of the plane dir should be the plane normal temporary marks will not be stored or randomly oriented, but immediately passed to the renderer. ================= */ #define MAX_MARK_FRAGMENTS 128 #define MAX_MARK_POINTS 384 void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir, float orientation, float red, float green, float blue, float alpha, qboolean alphaFade, float radius, qboolean temporary ) { vec3_t axis[3]; float texCoordScale; vec3_t originalPoints[4]; byte colors[4]; int i, j; int numFragments; markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; vec3_t markPoints[MAX_MARK_POINTS]; vec3_t projection; if ( !cg_addMarks.integer ) { return; } if ( radius <= 0 ) { CG_Error( "CG_ImpactMark called with <= 0 radius" ); } //if ( markTotal >= MAX_MARK_POLYS ) { // return; //} // create the texture axis VectorNormalize2( dir, axis[0] ); PerpendicularVector( axis[1], axis[0] ); if (cg_leiEnhancement.integer) // LEILEI HACK HACK HACK - don't spin atlas variated particles (for consistent lighting on the texture) orientation = 90; RotatePointAroundVector( axis[2], axis[0], axis[1], orientation ); CrossProduct( axis[0], axis[2], axis[1] ); texCoordScale = 0.5 * 1.0 / radius; // create the full polygon for ( i = 0 ; i < 3 ; i++ ) { originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; } // get the fragments VectorScale( dir, -20, projection ); numFragments = trap_CM_MarkFragments( 4, (void *)originalPoints, projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments ); colors[0] = red * 255; colors[1] = green * 255; colors[2] = blue * 255; colors[3] = alpha * 255; for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) { polyVert_t *v; polyVert_t verts[MAX_VERTS_ON_POLY]; markPoly_t *mark; // we have an upper limit on the complexity of polygons // that we store persistantly if ( mf->numPoints > MAX_VERTS_ON_POLY ) { mf->numPoints = MAX_VERTS_ON_POLY; } for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) { vec3_t delta; VectorCopy( markPoints[mf->firstPoint + j], v->xyz ); VectorSubtract( v->xyz, origin, delta ); v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale; v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale; *(int *)v->modulate = *(int *)colors; } // if it is a temporary (shadow) mark, add it immediately and forget about it if ( temporary ) { trap_R_AddPolyToScene( markShader, mf->numPoints, verts ); continue; } // otherwise save it persistantly mark = CG_AllocMark(); mark->time = cg.time; mark->alphaFade = alphaFade; mark->markShader = markShader; mark->poly.numVerts = mf->numPoints; mark->color[0] = red; mark->color[1] = green; mark->color[2] = blue; mark->color[3] = alpha; memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) ); markTotal++; } } /* =============== CG_AddMarks =============== */ #define MARK_TOTAL_TIME 10000 #define MARK_FADE_TIME 1000 void CG_AddMarks( void ) { int j; markPoly_t *mp, *next; int t; int fade; if ( !cg_addMarks.integer ) { return; } mp = cg_activeMarkPolys.nextMark; for ( ; mp != &cg_activeMarkPolys ; mp = next ) { // grab next now, so if the local entity is freed we // still have it next = mp->nextMark; // see if it is time to completely remove it if ( cg.time > mp->time + MARK_TOTAL_TIME ) { CG_FreeMarkPoly( mp ); continue; } // fade out the energy bursts if ( mp->markShader == cgs.media.energyMarkShader ) { fade = 450 - 450 * ( (cg.time - mp->time ) / 3000.0 ); if ( fade < 255 ) { if ( fade < 0 ) { fade = 0; } if ( mp->verts[0].modulate[0] != 0 ) { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[0] = mp->color[0] * fade; mp->verts[j].modulate[1] = mp->color[1] * fade; mp->verts[j].modulate[2] = mp->color[2] * fade; } } } } // fade all marks out with time t = mp->time + MARK_TOTAL_TIME - cg.time; if ( t < MARK_FADE_TIME ) { fade = 255 * t / MARK_FADE_TIME; if ( mp->alphaFade ) { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[3] = fade; } } else { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[0] = mp->color[0] * fade; mp->verts[j].modulate[1] = mp->color[1] * fade; mp->verts[j].modulate[2] = mp->color[2] * fade; } } } trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts ); } } // cg_particles.c #define BLOODRED 2 #define EMISIVEFADE 3 #define GREY75 4 typedef struct particle_s { struct particle_s *next; float time; float endtime; vec3_t org; vec3_t vel; vec3_t accel; int color; float colorvel; float alpha; float alphavel; int type; qhandle_t pshader; float height; float width; float endheight; float endwidth; float start; float end; float startfade; qboolean rotate; int snum; qboolean link; // Ridah int shaderAnim; int roll; int accumroll; } cparticle_t; typedef enum { P_NONE, P_WEATHER, P_FLAT, P_SMOKE, P_ROTATE, P_WEATHER_TURBULENT, P_ANIM, // Ridah P_BAT, P_BLEED, P_FLAT_SCALEUP, P_FLAT_SCALEUP_FADE, P_WEATHER_FLURRY, P_SMOKE_IMPACT, P_BUBBLE, P_BUBBLE_TURBULENT, P_SPRITE } particle_type_t; #define MAX_SHADER_ANIMS 32 #define MAX_SHADER_ANIM_FRAMES 64 static char *shaderAnimNames[MAX_SHADER_ANIMS] = { "explode1", NULL }; static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; static int shaderAnimCounts[MAX_SHADER_ANIMS] = { 23 }; static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { 1.0f }; static int numShaderAnims; // done. #define PARTICLE_GRAVITY 40 #define MAX_PARTICLES 1024 cparticle_t *active_particles, *free_particles; cparticle_t particles[MAX_PARTICLES]; int cl_numparticles = MAX_PARTICLES; qboolean initparticles = qfalse; vec3_t pvforward, pvright, pvup; vec3_t rforward, rright, rup; float oldtime; /* =============== CL_ClearParticles =============== */ void CG_ClearParticles (void) { int i; memset( particles, 0, sizeof(particles) ); free_particles = &particles[0]; active_particles = NULL; for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; VectorSet (color, 1.0, 1.0, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) invratio = 1; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) alpha = 1; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (pvup, ru); VectorCopy (pvright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); } // Ridah, made this static so it doesn't interfere with other files static float roll = 0.0; /* =============== CG_AddParticles =============== */ void CG_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int type; vec3_t rotate_ang; if (!initparticles) CG_ClearParticles (); VectorCopy( cg.refdef.viewaxis[0], pvforward ); VectorCopy( cg.refdef.viewaxis[1], pvright ); VectorCopy( cg.refdef.viewaxis[2], pvup ); vectoangles( cg.refdef.viewaxis[0], rotate_ang ); roll += ((cg.time - oldtime) * 0.1) ; rotate_ang[ROLL] += (roll*0.9); AngleVectors ( rotate_ang, rforward, rright, rup); oldtime = cg.time; active = NULL; tail = NULL; for (p=active_particles ; p ; p=next) { next = p->next; time = (cg.time - p->time)*0.001; alpha = p->alpha + time*p->alphavel; if (alpha <= 0) { // faded out p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_WEATHER_FLURRY) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_FLAT_SCALEUP_FADE) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { // temporary sprite CG_AddParticleToScene (p, p->org, alpha); p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } p->next = NULL; if (!tail) active = tail = p; else { tail->next = p; tail = p; } if (alpha > 1.0) alpha = 1; color = p->color; time2 = time*time; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; type = p->type; CG_AddParticleToScene (p, org, alpha); } active_particles = active; } /* ====================== CG_AddParticles ====================== */ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) { cparticle_t *p; qboolean turb = qtrue; if (!pshader) CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.90f; p->alphavel = 0; p->start = cent->currentState.origin2[0]; p->end = cent->currentState.origin2[1]; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->pshader = pshader; if (rand()%100 > 90) { p->height = 32; p->width = 32; p->alpha = 0.10f; } else { p->height = 1; p->width = 1; } p->vel[2] = -20; p->type = P_WEATHER_FLURRY; if (turb) p->vel[2] = -10; VectorCopy(cent->currentState.origin, p->org); p->org[0] = p->org[0]; p->org[1] = p->org[1]; p->org[2] = p->org[2]; p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); p->vel[2] += cent->currentState.angles[2]; if (turb) { p->accel[0] = crandom () * 16; p->accel[1] = crandom () * 16; } } void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; p->height = 1; p->width = 1; p->vel[2] = -50; if (turb) { p->type = P_WEATHER_TURBULENT; p->vel[2] = -50 * 1.3; } else { p->type = P_WEATHER; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; float randsize; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; randsize = 1 + (crandom() * 0.5); p->height = randsize; p->width = randsize; p->vel[2] = 50 + ( crandom() * 10 ); if (turb) { p->type = P_BUBBLE_TURBULENT; p->vel[2] = 50 * 1.3; } else { p->type = P_BUBBLE; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) { // using cent->density = enttime // cent->frame = startfade cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSmoke == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->color = 0; p->alpha = 1.0; p->alphavel = 0; p->start = cent->currentState.origin[2]; p->end = cent->currentState.origin2[2]; p->pshader = pshader; p->rotate = qfalse; p->height = 8; p->width = 8; p->endheight = 32; p->endwidth = 32; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[2] = 5; if (cent->currentState.frame == 1)// reverse gravity p->vel[2] *= -1; p->roll = 8 + (crandom() * 4); } void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 1.0; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->accel[2] = -60; p->vel[2] += -20; } /* ====================== CG_ParticleExplosion ====================== */ void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) { cparticle_t *p; int anim; if (animStr < (char *)10) CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); // find the animation string for (anim=0; shaderAnimNames[anim]; anim++) { if (!Q_stricmp( animStr, shaderAnimNames[anim] )) break; } if (!shaderAnimNames[anim]) { CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); return; } if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.5; p->alphavel = 0; if (duration < 0) { duration *= -1; p->roll = 0; } else { p->roll = crandom()*179; } p->shaderAnim = anim; p->width = sizeStart; p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction p->endheight = sizeEnd; p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; p->endtime = cg.time + duration; p->type = P_ANIM; VectorCopy( origin, p->org ); VectorCopy( vel, p->vel ); VectorClear( p->accel ); } // Rafael Shrapnel void CG_AddParticleShrapnel (localEntity_t *le) { return; } // done. int CG_NewParticleArea (int num) { // const char *str; char *str; char *token; int type; vec3_t origin, origin2; int i; float range = 0; int turb; int numparticles; int snum; str = (char *) CG_ConfigString (num); if (!str[0]) return (0); // returns type 128 64 or 32 token = COM_Parse (&str); type = atoi (token); if (type == 1) range = 128; else if (type == 2) range = 64; else if (type == 3) range = 32; else if (type == 0) range = 256; else if (type == 4) range = 8; else if (type == 5) range = 16; else if (type == 6) range = 32; else if (type == 7) range = 64; for (i=0; i<3; i++) { token = COM_Parse (&str); origin[i] = atof (token); } for (i=0; i<3; i++) { token = COM_Parse (&str); origin2[i] = atof (token); } token = COM_Parse (&str); numparticles = atoi (token); token = COM_Parse (&str); turb = atoi (token); token = COM_Parse (&str); snum = atoi (token); for (i=0; i= 4) CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); else CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); } return (1); } void CG_SnowLink (centity_t *cent, qboolean particleOn) { cparticle_t *p, *next; int id; id = cent->currentState.frame; for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) { if (p->snum == id) { if (particleOn) p->link = qtrue; else p->link = qfalse; } } } } void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.25; p->alphavel = 0; p->roll = crandom()*179; p->pshader = pshader; p->endtime = cg.time + 1000; p->startfade = cg.time + 100; p->width = rand()%4 + 8; p->height = rand()%4 + 8; p->endheight = p->height *2; p->endwidth = p->width * 2; p->endtime = cg.time + 500; p->type = P_SMOKE_IMPACT; VectorCopy( origin, p->org ); VectorSet(p->vel, 0, 0, 20); VectorSet(p->accel, 0, 0, 20); p->rotate = qtrue; } void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; if (fleshEntityNum) p->startfade = cg.time; else p->startfade = cg.time + 100; p->width = 4; p->height = 4; p->endheight = 4+rand()%3; p->endwidth = p->endheight; p->type = P_SMOKE; VectorCopy( start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -20; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) { cparticle_t *p; int time; int time2; float ratio; float duration = 1500; time = cg.time; time2 = cg.time + cent->currentState.time; ratio =(float)1 - ((float)time / (float)time2); if (!pshader) CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; p->startfade = p->endtime; p->width = 1; p->height = 3; p->endheight = 3; p->endwidth = 1; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org ); p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); p->vel[2] = (cent->currentState.origin2[2]); p->snum = 1.0f; VectorClear( p->accel ); p->accel[2] = -20; p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; if (cent->currentState.angles2[2]) p->endtime = cg.time + cent->currentState.angles2[2]; else p->endtime = cg.time + 60000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) { p->width = cent->currentState.angles2[0]; p->height = cent->currentState.angles2[0]; p->endheight = cent->currentState.angles2[1]; p->endwidth = cent->currentState.angles2[1]; } else { p->width = 8; p->height = 8; p->endheight = 16; p->endwidth = 16; } p->type = P_FLAT_SCALEUP; p->snum = 1.0; VectorCopy(cent->currentState.origin, p->org ); p->org[2]+= 0.55 + (crandom() * 0.5); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_OilSlickRemove (centity_t *cent) { cparticle_t *p, *next; int id; id = 1.0f; if (!id) CG_Printf ("CG_OilSlickRevove NULL id\n"); for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_FLAT_SCALEUP) { if (p->snum == id) { p->endtime = cg.time + 100; p->startfade = p->endtime; p->type = P_FLAT_SCALEUP_FADE; } } } } qboolean ValidBloodPool (vec3_t start) { #define EXTRUDE_DIST 0.5 vec3_t angles; vec3_t right, up; vec3_t this_pos, x_pos, center_pos, end_pos; float x, y; float fwidth, fheight; trace_t trace; vec3_t normal; fwidth = 16; fheight = 16; VectorSet (normal, 0, 0, 1); vectoangles (normal, angles); AngleVectors (angles, NULL, right, up); VectorMA (start, EXTRUDE_DIST, normal, center_pos); for (x= -fwidth/2; xendpos, start); legit = ValidBloodPool (start); if (!legit) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + 3000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; rndSize = 0.4 + random()*0.6; p->width = 8*rndSize; p->height = 8*rndSize; p->endheight = 16*rndSize; p->endwidth = 16*rndSize; p->type = P_FLAT_SCALEUP; VectorCopy(start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; p->color = BLOODRED; } #define NORMALSIZE 16 #define LARGESIZE 32 void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } } void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.4f; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 4); p->vel[1] += (crandom() * 4); p->vel[2] += (20 + (crandom() * 10)) * speed; p->accel[0] = crandom () * 4; p->accel[1] = crandom () * 4; } void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } } void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = rand()%179; p->pshader = pshader; if (duration > 0) p->endtime = cg.time + duration; else p->endtime = duration; p->startfade = cg.time; p->width = size; p->height = size; p->endheight = size; p->endwidth = size; p->type = P_SPRITE; VectorCopy( origin, p->org ); p->rotate = qfalse; } // LEILEI ENHANCEMENT PARTICLE EFFECTS // sparks! // for small arms void CG_LeiSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.8f; p->alphavel = 0.8f; p->height = 4; p->width = 4; p->endheight = 4; p->endwidth = 4; p->pshader = cgs.media.lspkShader1; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0] * 75; p->vel[1] = vel[1] * 75; p->vel[2] = vel[2] * 75; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * speed); p->vel[1] += (crandom() * speed); p->vel[2] += speed + (crandom() * speed); p->vel[0] += (crandom() * 24); p->vel[1] += (crandom() * 24); p->vel[2] += (20 + (crandom() * 180)); p->accel[0] = crandom()*6; p->accel[1] = crandom()*6; p->accel[2] = -PARTICLE_GRAVITY*7.2; } // a different sort of puff void CG_LeiPuff (vec3_t org, vec3_t vel, int duration, float x, float y, float speed, float size) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.8f; p->alphavel = 0.8f; p->height = size; p->width = size; p->endheight = size * 1.8; p->endwidth = size * 1.8; p->pshader = cgs.media.lspkShader1; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0] * speed; p->vel[1] = vel[1] * speed; p->vel[2] = vel[2] * speed; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 44); p->vel[1] += (crandom() * 44); p->vel[2] += (crandom() * 44); p->roll = (crandom() * 256 - 128); // p->vel[0] += (crandom() * 24); //p->vel[1] += (crandom() * 24); //p->vel[2] += (20 + (crandom() * 180)) * speed; p->accel[0] = -2; p->accel[1] = -2; p->accel[2] = -2; } // a violent blast puff void CG_LeiBlast (vec3_t org, vec3_t vel, int duration, float x, float y, float speed, float size) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 1.0f; p->alphavel = 0.72f; p->height = size; p->width = size; p->endheight = size * 6; p->endwidth = size * 6; p->pshader = cgs.media.lbumShader1; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0] * speed; p->vel[1] = vel[1] * speed; p->vel[2] = vel[2] * speed; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 84); p->vel[1] += (crandom() * 84); p->vel[2] += (crandom() * 84); p->roll = (crandom() * 256 - 128); // p->vel[0] += (crandom() * 24); //p->vel[1] += (crandom() * 24); //p->vel[2] += (20 + (crandom() * 180)) * speed; p->accel[0] = -2; p->accel[1] = -2; p->accel[2] = -2; } // for explosions void CG_LeiSparks2 (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.8f; p->alphavel = 0; p->height = 9; p->width = 9; p->endheight = 32; p->endwidth = 32; p->pshader = cgs.media.lspkShader1; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0] * 15; p->vel[1] = vel[1] * 15; p->vel[2] = vel[2] * 15; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 524); p->vel[1] += (crandom() * 524); p->vel[2] += (120 + (crandom() * 780)) * speed; // p->accel[0] = crandom()*76; // p->accel[1] = crandom()*76; // p->accel[2] = crandom()*76; // VectorCopy( origin, p->org ); // VectorCopy( vel, p->vel ); // VectorClear( p->accel ); } // not so friendly water splash void CG_LeiSplash2 (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.9f; p->alphavel = 0; p->height = 4; p->width = 4; p->endheight = 2; p->endwidth = 2; p->pshader = cgs.media.lsplShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0] * 44; p->vel[1] = vel[1] * 44; p->vel[2] = vel[2] * 872; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 4); p->vel[1] += (crandom() * 4); p->vel[2] += (20 + (crandom() * 10)) * speed; p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*4.2; } openarena_0.8.8.orig/code/cgame/cg_servercmds.c0000644000175000017500000011076211656310265020216 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_servercmds.c -- reliably sequenced text commands sent by the server // these are processed at snapshot transition time, so there will definately // be a valid snapshot this frame #include "cg_local.h" #include "../../ui/menudef.h" // bk001205 - for Q3_ui as well typedef struct { const char *order; int taskNum; } orderTask_t; static const orderTask_t validOrders[] = { { VOICECHAT_GETFLAG, TEAMTASK_OFFENSE }, { VOICECHAT_OFFENSE, TEAMTASK_OFFENSE }, { VOICECHAT_DEFEND, TEAMTASK_DEFENSE }, { VOICECHAT_DEFENDFLAG, TEAMTASK_DEFENSE }, { VOICECHAT_PATROL, TEAMTASK_PATROL }, { VOICECHAT_CAMP, TEAMTASK_CAMP }, { VOICECHAT_FOLLOWME, TEAMTASK_FOLLOW }, { VOICECHAT_RETURNFLAG, TEAMTASK_RETRIEVE }, { VOICECHAT_FOLLOWFLAGCARRIER, TEAMTASK_ESCORT } }; static const int numValidOrders = sizeof(validOrders) / sizeof(orderTask_t); #ifdef MISSIONPACK // bk001204 static int CG_ValidOrder(const char *p) { int i; for (i = 0; i < numValidOrders; i++) { if (Q_stricmp(p, validOrders[i].order) == 0) { return validOrders[i].taskNum; } } return -1; } #endif /* ================= CG_ParseScores ================= */ static void CG_ParseScores( void ) { int i, powerups; cg.numScores = atoi( CG_Argv( 1 ) ); if ( cg.numScores > MAX_CLIENTS ) { cg.numScores = MAX_CLIENTS; } cg.teamScores[0] = atoi( CG_Argv( 2 ) ); cg.teamScores[1] = atoi( CG_Argv( 3 ) ); cgs.roundStartTime = atoi( CG_Argv( 4 ) ); //Update thing in lower-right corner if(cgs.gametype == GT_ELIMINATION || cgs.gametype == GT_CTF_ELIMINATION) { cgs.scores1 = cg.teamScores[0]; cgs.scores2 = cg.teamScores[1]; } memset( cg.scores, 0, sizeof( cg.scores ) ); #define NUM_DATA 15 #define FIRST_DATA 4 for ( i = 0 ; i < cg.numScores ; i++ ) { // cg.scores[i].client = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 1 ) ); cg.scores[i].score = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 2 ) ); cg.scores[i].ping = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 3 ) ); cg.scores[i].time = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 4 ) ); cg.scores[i].scoreFlags = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 5 ) ); powerups = atoi( CG_Argv( i * NUM_DATA + FIRST_DATA + 6 ) ); cg.scores[i].accuracy = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 7)); cg.scores[i].impressiveCount = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 8)); cg.scores[i].excellentCount = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 9)); cg.scores[i].guantletCount = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 10)); cg.scores[i].defendCount = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 11)); cg.scores[i].assistCount = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 12)); cg.scores[i].perfect = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 13)); cg.scores[i].captures = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 14)); cg.scores[i].isDead = atoi(CG_Argv(i * NUM_DATA + FIRST_DATA + 15)); //cgs.roundStartTime = if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) { cg.scores[i].client = 0; } cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score; cgs.clientinfo[ cg.scores[i].client ].powerups = powerups; cgs.clientinfo[ cg.scores[i].client ].isDead = cg.scores[i].isDead; cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team; } #ifdef MISSIONPACK CG_SetScoreSelection(NULL); #endif } static void CG_ParseAccuracy( void ) { int i; for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) { cg.accuracys[i-WP_MACHINEGUN][0] = atoi( CG_Argv( (i-WP_MACHINEGUN)*2 + 1 ) ); cg.accuracys[i-WP_MACHINEGUN][1] = atoi( CG_Argv( (i-WP_MACHINEGUN)*2 + 2 ) ); #if DEBUG CG_Printf("W: %i shots: %i Hits: %i\n", i,cg.accuracys[i][0], cg.accuracys[i][1]); #endif } } /* ================= CG_ParseElimination ================= */ static void CG_ParseElimination( void ) { if(cgs.gametype == GT_ELIMINATION || cgs.gametype == GT_CTF_ELIMINATION) { cgs.scores1 = atoi( CG_Argv( 1 ) ); cgs.scores2 = atoi( CG_Argv( 2 ) ); } cgs.roundStartTime = atoi( CG_Argv( 3 ) ); } /* ================= CG_ParseMappage Sago: This parses values from the server rather directly. Some checks are performed, but beware if you change it or new security holes are found ================= */ static void CG_ParseMappage( void ) { char command[1024]; const char *temp; const char* c; int i; temp = CG_Argv( 1 ); for( c = temp; *c; ++c) { switch(*c) { case '\n': case '\r': case ';': //The server tried something bad! return; break; } } Q_strncpyz(command,va("ui_mappage %s",temp),1024); for(i=2;i<12;i++) { temp = CG_Argv( i ); for( c = temp; *c; ++c) { switch(*c) { case '\n': case '\r': case ';': //The server tried something bad! return; break; } } if(strlen(temp)<1) temp = "---"; Q_strcat(command,1024,va(" %s ",temp)); } trap_SendConsoleCommand(command); } /* ================= CG_ParseDDtimetaken ================= */ static void CG_ParseDDtimetaken( void ) { cgs.timetaken = atoi( CG_Argv( 1 ) ); } /* ================= CG_ParseDomPointNames ================= */ static void CG_ParseDomPointNames( void ) { int i,j; cgs.domination_points_count = atoi( CG_Argv( 1 ) ); if(cgs.domination_points_count>=MAX_DOMINATION_POINTS) cgs.domination_points_count = MAX_DOMINATION_POINTS; for(i = 0;i0; j--) { cgs.domination_points_names[i][j] = 0; } } } /* ================= CG_ParseDomScores ================= */ static void CG_ParseDomStatus( void ) { int i; if( cgs.domination_points_count!=atoi( CG_Argv(1) ) ) { cgs.domination_points_count = 0; return; } for(i = 0;i TEAM_MAXOVERLAY ) { CG_Error( "CG_ParseTeamInfo: numSortedTeamPlayers out of range (%d)", numSortedTeamPlayers ); return; } for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) { client = atoi( CG_Argv( i * 6 + 2 ) ); if( client < 0 || client >= MAX_CLIENTS ) { CG_Error( "CG_ParseTeamInfo: bad client number: %d", client ); return; } sortedTeamPlayers[i] = client; cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) ); cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) ); cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) ); cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) ); cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) ); } } /* ================ CG_ParseServerinfo This is called explicitly when the gamestate is first received, and whenever the server updates any serverinfo flagged cvars ================ */ void CG_ParseServerinfo( void ) { const char *info; char *mapname; info = CG_ConfigString( CS_SERVERINFO ); cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); //By default do as normal: cgs.ffa_gt = 0; //See if ffa gametype if(cgs.gametype == GT_LMS) cgs.ffa_gt = 1; trap_Cvar_Set("g_gametype", va("%i", cgs.gametype)); cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) ); cgs.videoflags = atoi( Info_ValueForKey( info, "videoflags" ) ); cgs.elimflags = atoi( Info_ValueForKey( info, "elimflags" ) ); cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) ); cgs.fraglimit = atoi( Info_ValueForKey( info, "fraglimit" ) ); cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) ); cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); cgs.roundtime = atoi( Info_ValueForKey( info, "elimination_roundtime" ) ); cgs.nopickup = atoi( Info_ValueForKey( info, "g_rockets" ) ) + atoi( Info_ValueForKey( info, "g_instantgib" ) ) + atoi( Info_ValueForKey( info, "g_elimination" ) ); cgs.lms_mode = atoi( Info_ValueForKey( info, "g_lms_mode" ) ); cgs.altExcellent = atoi( Info_ValueForKey( info, "g_altExcellent" ) ); mapname = Info_ValueForKey( info, "mapname" ); Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); Q_strncpyz( cgs.redTeam, Info_ValueForKey( info, "g_redTeam" ), sizeof(cgs.redTeam) ); trap_Cvar_Set("g_redTeam", cgs.redTeam); Q_strncpyz( cgs.blueTeam, Info_ValueForKey( info, "g_blueTeam" ), sizeof(cgs.blueTeam) ); trap_Cvar_Set("g_blueTeam", cgs.blueTeam); //unlagged - server options // we'll need this for deciding whether or not to predict weapon effects cgs.delagHitscan = atoi( Info_ValueForKey( info, "g_delagHitscan" ) ); trap_Cvar_Set("g_delagHitscan", va("%i", cgs.delagHitscan)); //unlagged - server options //Copy allowed votes directly to the client: trap_Cvar_Set("cg_voteflags",Info_ValueForKey( info, "voteflags" ) ); } /* ================== CG_ParseWarmup ================== */ static void CG_ParseWarmup( void ) { const char *info; int warmup; info = CG_ConfigString( CS_WARMUP ); warmup = atoi( info ); cg.warmupCount = -1; if ( warmup == 0 && cg.warmup ) { } else if ( warmup > 0 && cg.warmup <= 0 ) { #ifdef MISSIONPACK if (cgs.gametype >= GT_CTF && cgs.gametype < GT_MAX_GAME_TYPE && !cgs.ffa_gt) { trap_S_StartLocalSound( cgs.media.countPrepareTeamSound, CHAN_ANNOUNCER ); } else #endif { trap_S_StartLocalSound( cgs.media.countPrepareSound, CHAN_ANNOUNCER ); } } cg.warmup = warmup; } /* ================ CG_SetConfigValues Called on load to set the initial values from configure strings ================ */ void CG_SetConfigValues( void ) { const char *s; cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) ); cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) ); cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) ); if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_DOUBLE_D) { s = CG_ConfigString( CS_FLAGSTATUS ); cgs.redflag = s[0] - '0'; cgs.blueflag = s[1] - '0'; } //#ifdef MISSIONPACK else if( cgs.gametype == GT_1FCTF ) { s = CG_ConfigString( CS_FLAGSTATUS ); cgs.flagStatus = s[0] - '0'; } //#endif cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); } /* ===================== CG_ShaderStateChanged ===================== */ void CG_ShaderStateChanged(void) { char originalShader[MAX_QPATH]; char newShader[MAX_QPATH]; char timeOffset[16]; const char *o; char *n,*t; o = CG_ConfigString( CS_SHADERSTATE ); while (o && *o) { n = strstr(o, "="); if (n && *n) { strncpy(originalShader, o, n-o); originalShader[n-o] = 0; n++; t = strstr(n, ":"); if (t && *t) { strncpy(newShader, n, t-n); newShader[t-n] = 0; } else { break; } t++; o = strstr(t, "@"); if (o) { strncpy(timeOffset, t, o-t); timeOffset[o-t] = 0; o++; trap_R_RemapShader( originalShader, newShader, timeOffset ); } } else { break; } } } /* ================ CG_ConfigStringModified ================ */ static void CG_ConfigStringModified( void ) { const char *str; int num; num = atoi( CG_Argv( 1 ) ); // get the gamestate from the client system, which will have the // new configstring already integrated trap_GetGameState( &cgs.gameState ); // look up the individual string that was modified str = CG_ConfigString( num ); // do something with it if necessary if ( num == CS_MUSIC ) { CG_StartMusic(); } else if ( num == CS_SERVERINFO ) { CG_ParseServerinfo(); } else if ( num == CS_WARMUP ) { CG_ParseWarmup(); } else if ( num == CS_SCORES1 ) { cgs.scores1 = atoi( str ); } else if ( num == CS_SCORES2 ) { cgs.scores2 = atoi( str ); } else if ( num == CS_LEVEL_START_TIME ) { cgs.levelStartTime = atoi( str ); } else if ( num == CS_VOTE_TIME ) { cgs.voteTime = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_YES ) { cgs.voteYes = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_NO ) { cgs.voteNo = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_STRING ) { Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) ); #ifdef MISSIONPACK trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER ); #endif //MISSIONPACK } else if ( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1) { cgs.teamVoteTime[num-CS_TEAMVOTE_TIME] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_TIME] = qtrue; } else if ( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1) { cgs.teamVoteYes[num-CS_TEAMVOTE_YES] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_YES] = qtrue; } else if ( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1) { cgs.teamVoteNo[num-CS_TEAMVOTE_NO] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_NO] = qtrue; } else if ( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1) { Q_strncpyz( cgs.teamVoteString[num-CS_TEAMVOTE_STRING], str, sizeof( cgs.teamVoteString ) ); #ifdef MISSIONPACK trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER ); #endif } else if ( num == CS_INTERMISSION ) { cg.intermissionStarted = atoi( str ); } else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) { cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str ); } else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) { if ( str[0] != '*' ) { // player specific sounds don't register here cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str, qfalse ); } } else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) { CG_NewClientInfo( num - CS_PLAYERS ); CG_BuildSpectatorString(); } else if ( num == CS_FLAGSTATUS ) { if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_DOUBLE_D) { // format is rb where its red/blue, 0 is at base, 1 is taken, 2 is dropped cgs.redflag = str[0] - '0'; cgs.blueflag = str[1] - '0'; } //#ifdef MISSIONPACK else if( cgs.gametype == GT_1FCTF ) { cgs.flagStatus = str[0] - '0'; } //#endif } else if ( num == CS_SHADERSTATE ) { CG_ShaderStateChanged(); } } /* ======================= CG_AddToTeamChat ======================= */ static void CG_AddToTeamChat( const char *str ) { int len; char *p, *ls; int lastcolor; int chatHeight; if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) { chatHeight = cg_teamChatHeight.integer; } else { chatHeight = TEAMCHAT_HEIGHT; } if (chatHeight <= 0 || cg_teamChatTime.integer <= 0) { // team chat disabled, dump into normal chat cgs.teamChatPos = cgs.teamLastChatPos = 0; return; } len = 0; p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; *p = 0; lastcolor = '7'; ls = NULL; while (*str) { if (len > TEAMCHAT_WIDTH - 1) { if (ls) { str -= (p - ls); str++; p -= (p - ls); } *p = 0; cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; cgs.teamChatPos++; p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; *p = 0; *p++ = Q_COLOR_ESCAPE; *p++ = lastcolor; len = 0; ls = NULL; } if ( Q_IsColorString( str ) ) { *p++ = *str++; lastcolor = *str; *p++ = *str++; continue; } if (*str == ' ') { ls = p; } *p++ = *str++; len++; } *p = 0; cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; cgs.teamChatPos++; if (cgs.teamChatPos - cgs.teamLastChatPos > chatHeight) cgs.teamLastChatPos = cgs.teamChatPos - chatHeight; } /* =============== CG_MapRestart The server has issued a map_restart, so the next snapshot is completely new and should not be interpolated to. A tournement restart will clear everything, but doesn't require a reload of all the media =============== */ static void CG_MapRestart( void ) { if ( cg_showmiss.integer ) { CG_Printf( "CG_MapRestart\n" ); } CG_InitLocalEntities(); CG_InitMarkPolys(); CG_ClearParticles (); // make sure the "3 frags left" warnings play again cg.fraglimitWarnings = 0; cg.timelimitWarnings = 0; cg.intermissionStarted = qfalse; cgs.voteTime = 0; cg.mapRestart = qtrue; CG_StartMusic(); trap_S_ClearLoopingSounds(qtrue); // we really should clear more parts of cg here and stop sounds // play the "fight" sound if this is a restart without warmup if ( cg.warmup == 0 /* && cgs.gametype == GT_TOURNAMENT */) { trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER ); CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH*2 ); } #ifdef MISSIONPACK if (cg_singlePlayerActive.integer) { trap_Cvar_Set("ui_matchStartTime", va("%i", cg.time)); if (cg_recordSPDemo.integer && cg_recordSPDemoName.string && *cg_recordSPDemoName.string) { trap_SendConsoleCommand(va("set g_synchronousclients 1 ; record %s \n", cg_recordSPDemoName.string)); } } #endif trap_Cvar_Set("cg_thirdPerson", "0"); } #define MAX_VOICEFILESIZE 16384 #define MAX_VOICEFILES 8 #define MAX_VOICECHATS 64 #define MAX_VOICESOUNDS 64 #define MAX_CHATSIZE 64 #define MAX_HEADMODELS 64 typedef struct voiceChat_s { char id[64]; int numSounds; sfxHandle_t sounds[MAX_VOICESOUNDS]; char chats[MAX_VOICESOUNDS][MAX_CHATSIZE]; } voiceChat_t; typedef struct voiceChatList_s { char name[64]; int gender; int numVoiceChats; voiceChat_t voiceChats[MAX_VOICECHATS]; } voiceChatList_t; typedef struct headModelVoiceChat_s { char headmodel[64]; int voiceChatNum; } headModelVoiceChat_t; voiceChatList_t voiceChatLists[MAX_VOICEFILES]; headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS]; /* ================= CG_ParseVoiceChats ================= */ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) { int len, i; fileHandle_t f; char buf[MAX_VOICEFILESIZE]; char **p, *ptr; char *token; voiceChat_t *voiceChats; qboolean compress; sfxHandle_t sound; compress = qtrue; if (cg_buildScript.integer) { compress = qfalse; } len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) ); return qfalse; } if ( len >= MAX_VOICEFILESIZE ) { trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i\n", filename, len, MAX_VOICEFILESIZE ) ); trap_FS_FCloseFile( f ); return qfalse; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ptr = buf; p = &ptr; Com_sprintf(voiceChatList->name, sizeof(voiceChatList->name), "%s", filename); voiceChats = voiceChatList->voiceChats; for ( i = 0; i < maxVoiceChats; i++ ) { voiceChats[i].id[0] = 0; } token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } if (!Q_stricmp(token, "female")) { voiceChatList->gender = GENDER_FEMALE; } else if (!Q_stricmp(token, "male")) { voiceChatList->gender = GENDER_MALE; } else if (!Q_stricmp(token, "neuter")) { voiceChatList->gender = GENDER_NEUTER; } else { trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) ); return qfalse; } voiceChatList->numVoiceChats = 0; while ( 1 ) { token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token); token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "{")) { trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) ); return qfalse; } voiceChats[voiceChatList->numVoiceChats].numSounds = 0; while(1) { token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } if (!Q_stricmp(token, "}")) break; sound = trap_S_RegisterSound( token, compress ); voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = sound; token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[ voiceChats[voiceChatList->numVoiceChats].numSounds], MAX_CHATSIZE, "%s", token); if (sound) voiceChats[voiceChatList->numVoiceChats].numSounds++; if (voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS) break; } voiceChatList->numVoiceChats++; if (voiceChatList->numVoiceChats >= maxVoiceChats) return qtrue; } return qtrue; } /* ================= CG_LoadVoiceChats ================= */ void CG_LoadVoiceChats( void ) { int size; size = trap_MemoryRemaining(); CG_ParseVoiceChats( "scripts/female1.voice", &voiceChatLists[0], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/female2.voice", &voiceChatLists[1], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/female3.voice", &voiceChatLists[2], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male1.voice", &voiceChatLists[3], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male2.voice", &voiceChatLists[4], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male3.voice", &voiceChatLists[5], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male4.voice", &voiceChatLists[6], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male5.voice", &voiceChatLists[7], MAX_VOICECHATS ); CG_Printf("voice chat memory size = %d\n", size - trap_MemoryRemaining()); } /* ================= CG_HeadModelVoiceChats ================= */ int CG_HeadModelVoiceChats( char *filename ) { int len, i; fileHandle_t f; char buf[MAX_VOICEFILESIZE]; char **p, *ptr; char *token; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { //trap_Print( va( "voice chat file not found: %s\n", filename ) ); return -1; } if ( len >= MAX_VOICEFILESIZE ) { trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i\n", filename, len, MAX_VOICEFILESIZE ) ); trap_FS_FCloseFile( f ); return -1; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ptr = buf; p = &ptr; token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return -1; } for ( i = 0; i < MAX_VOICEFILES; i++ ) { if ( !Q_stricmp(token, voiceChatLists[i].name) ) { return i; } } //FIXME: maybe try to load the .voice file which name is stored in token? return -1; } /* ================= CG_GetVoiceChat ================= */ int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, char **chat) { int i, rnd; for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) { if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) { rnd = random() * voiceChatList->voiceChats[i].numSounds; *snd = voiceChatList->voiceChats[i].sounds[rnd]; *chat = voiceChatList->voiceChats[i].chats[rnd]; return qtrue; } } return qfalse; } /* ================= CG_VoiceChatListForClient ================= */ voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) { clientInfo_t *ci; int voiceChatNum, i, j, k, gender; char filename[MAX_QPATH], headModelName[MAX_QPATH]; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; for ( k = 0; k < 2; k++ ) { if ( k == 0 ) { if (ci->headModelName[0] == '*') { Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName+1, ci->headSkinName ); } else { Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName, ci->headSkinName ); } } else { if (ci->headModelName[0] == '*') { Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName+1 ); } else { Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName ); } } // find the voice file for the head model the client uses for ( i = 0; i < MAX_HEADMODELS; i++ ) { if (!Q_stricmp(headModelVoiceChat[i].headmodel, headModelName)) { break; } } if (i < MAX_HEADMODELS) { return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; } // find a .vc file for ( i = 0; i < MAX_HEADMODELS; i++ ) { if (!strlen(headModelVoiceChat[i].headmodel)) { Com_sprintf(filename, sizeof(filename), "scripts/%s.vc", headModelName); voiceChatNum = CG_HeadModelVoiceChats(filename); if (voiceChatNum == -1) break; Com_sprintf(headModelVoiceChat[i].headmodel, sizeof ( headModelVoiceChat[i].headmodel ), "%s", headModelName); headModelVoiceChat[i].voiceChatNum = voiceChatNum; return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; } } } gender = ci->gender; for (k = 0; k < 2; k++) { // just pick the first with the right gender for ( i = 0; i < MAX_VOICEFILES; i++ ) { if (strlen(voiceChatLists[i].name)) { if (voiceChatLists[i].gender == gender) { // store this head model with voice chat for future reference for ( j = 0; j < MAX_HEADMODELS; j++ ) { if (!strlen(headModelVoiceChat[j].headmodel)) { Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), "%s", headModelName); headModelVoiceChat[j].voiceChatNum = i; break; } } return &voiceChatLists[i]; } } } // fall back to male gender because we don't have neuter in the mission pack if (gender == GENDER_MALE) break; gender = GENDER_MALE; } // store this head model with voice chat for future reference for ( j = 0; j < MAX_HEADMODELS; j++ ) { if (!strlen(headModelVoiceChat[j].headmodel)) { Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), "%s", headModelName); headModelVoiceChat[j].voiceChatNum = 0; break; } } // just return the first voice chat list return &voiceChatLists[0]; } #define MAX_VOICECHATBUFFER 32 typedef struct bufferedVoiceChat_s { int clientNum; sfxHandle_t snd; int voiceOnly; char cmd[MAX_SAY_TEXT]; char message[MAX_SAY_TEXT]; } bufferedVoiceChat_t; bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER]; /* ================= CG_PlayVoiceChat ================= */ void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) { #ifdef MISSIONPACK // if we are going into the intermission, don't start any voices if ( cg.intermissionStarted ) { return; } if ( !cg_noVoiceChats.integer ) { trap_S_StartLocalSound( vchat->snd, CHAN_VOICE); if (vchat->clientNum != cg.snap->ps.clientNum) { int orderTask = CG_ValidOrder(vchat->cmd); if (orderTask > 0) { cgs.acceptOrderTime = cg.time + 5000; Q_strncpyz(cgs.acceptVoice, vchat->cmd, sizeof(cgs.acceptVoice)); cgs.acceptTask = orderTask; cgs.acceptLeader = vchat->clientNum; } // see if this was an order CG_ShowResponseHead(); } } if (!vchat->voiceOnly && !cg_noVoiceText.integer) { CG_AddToTeamChat( vchat->message ); CG_Printf( "%s\n", vchat->message ); } voiceChatBuffer[cg.voiceChatBufferOut].snd = 0; #endif } /* ===================== CG_PlayBufferedVoieChats ===================== */ void CG_PlayBufferedVoiceChats( void ) { #ifdef MISSIONPACK if ( cg.voiceChatTime < cg.time ) { if (cg.voiceChatBufferOut != cg.voiceChatBufferIn && voiceChatBuffer[cg.voiceChatBufferOut].snd) { // CG_PlayVoiceChat(&voiceChatBuffer[cg.voiceChatBufferOut]); // cg.voiceChatBufferOut = (cg.voiceChatBufferOut + 1) % MAX_VOICECHATBUFFER; cg.voiceChatTime = cg.time + 1000; } } #endif } /* ===================== CG_AddBufferedVoiceChat ===================== */ void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) { #ifdef MISSIONPACK // if we are going into the intermission, don't start any voices if ( cg.intermissionStarted ) { return; } memcpy(&voiceChatBuffer[cg.voiceChatBufferIn], vchat, sizeof(bufferedVoiceChat_t)); cg.voiceChatBufferIn = (cg.voiceChatBufferIn + 1) % MAX_VOICECHATBUFFER; if (cg.voiceChatBufferIn == cg.voiceChatBufferOut) { CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] ); cg.voiceChatBufferOut++; } #endif } /* ================= CG_VoiceChatLocal ================= */ void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ) { #ifdef MISSIONPACK char *chat; voiceChatList_t *voiceChatList; clientInfo_t *ci; sfxHandle_t snd; bufferedVoiceChat_t vchat; // if we are going into the intermission, don't start any voices if ( cg.intermissionStarted ) { return; } if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; cgs.currentVoiceClient = clientNum; voiceChatList = CG_VoiceChatListForClient( clientNum ); if ( CG_GetVoiceChat( voiceChatList, cmd, &snd, &chat ) ) { // if ( mode == SAY_TEAM || !cg_teamChatsOnly.integer ) { vchat.clientNum = clientNum; vchat.snd = snd; vchat.voiceOnly = voiceOnly; Q_strncpyz(vchat.cmd, cmd, sizeof(vchat.cmd)); if ( mode == SAY_TELL ) { Com_sprintf(vchat.message, sizeof(vchat.message), "[%s]: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); } else if ( mode == SAY_TEAM ) { Com_sprintf(vchat.message, sizeof(vchat.message), "(%s): %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); } else { Com_sprintf(vchat.message, sizeof(vchat.message), "%s: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); } CG_AddBufferedVoiceChat(&vchat); } } #endif } /* ================= CG_VoiceChat ================= */ void CG_VoiceChat( int mode ) { #ifdef MISSIONPACK const char *cmd; int clientNum, color; qboolean voiceOnly; voiceOnly = atoi(CG_Argv(1)); clientNum = atoi(CG_Argv(2)); color = atoi(CG_Argv(3)); cmd = CG_Argv(4); if (cg_noTaunt.integer != 0) { if (!strcmp(cmd, VOICECHAT_KILLINSULT) || !strcmp(cmd, VOICECHAT_TAUNT) || \ !strcmp(cmd, VOICECHAT_DEATHINSULT) || !strcmp(cmd, VOICECHAT_KILLGAUNTLET) || \ !strcmp(cmd, VOICECHAT_PRAISE)) { return; } } CG_VoiceChatLocal( mode, voiceOnly, clientNum, color, cmd ); #endif } /* ================= CG_RemoveChatEscapeChar ================= */ static void CG_RemoveChatEscapeChar( char *text ) { int i, l; l = 0; for ( i = 0; text[i]; i++ ) { if (text[i] == '\x19') continue; text[l++] = text[i]; } text[l] = '\0'; } /* ================= CG_ServerCommand The string has been tokenized and can be retrieved with Cmd_Argc() / Cmd_Argv() ================= */ static void CG_ServerCommand( void ) { const char *cmd; char text[MAX_SAY_TEXT]; cmd = CG_Argv(0); if ( !cmd[0] ) { // server claimed the command return; } if ( !strcmp( cmd, "cp" ) ) { CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); return; } if ( !strcmp( cmd, "cs" ) ) { CG_ConfigStringModified(); return; } if ( !strcmp( cmd, "print" ) ) { CG_Printf( "%s", CG_Argv(1) ); #ifdef MISSIONPACK cmd = CG_Argv(1); // yes, this is obviously a hack, but so is the way we hear about // votes passing or failing if ( !Q_stricmpn( cmd, "vote failed", 11 ) || !Q_stricmpn( cmd, "team vote failed", 16 )) { trap_S_StartLocalSound( cgs.media.voteFailed, CHAN_ANNOUNCER ); } else if ( !Q_stricmpn( cmd, "vote passed", 11 ) || !Q_stricmpn( cmd, "team vote passed", 16 ) ) { trap_S_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER ); } #endif return; } if ( !strcmp( cmd, "chat" ) ) { if ( !cg_teamChatsOnly.integer ) { if( cg_chatBeep.integer ) trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_Printf( "%s\n", text ); } return; } if ( !strcmp( cmd, "tchat" ) ) { if( cg_teamChatBeep.integer ) trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_AddToTeamChat( text ); CG_Printf( "%s\n", text ); return; } if ( !strcmp( cmd, "vchat" ) ) { CG_VoiceChat( SAY_ALL ); return; } if ( !strcmp( cmd, "vtchat" ) ) { CG_VoiceChat( SAY_TEAM ); return; } if ( !strcmp( cmd, "vtell" ) ) { CG_VoiceChat( SAY_TELL ); return; } if ( !strcmp( cmd, "scores" ) ) { CG_ParseScores(); return; } if ( !strcmp( cmd, "accs" ) ) { CG_ParseAccuracy(); return; } if ( !strcmp( cmd, "ddtaken" ) ) { CG_ParseDDtimetaken(); return; } if ( !strcmp( cmd, "dompointnames" ) ) { CG_ParseDomPointNames(); return; } if ( !strcmp( cmd, "domStatus" ) ) { CG_ParseDomStatus(); return; } if ( !strcmp( cmd, "elimination" ) ) { CG_ParseElimination(); return; } if ( !strcmp( cmd, "mappage" ) ) { CG_ParseMappage(); return; } if ( !strcmp( cmd, "attackingteam" ) ) { CG_ParseAttackingTeam(); return; } if ( !strcmp( cmd, "tinfo" ) ) { CG_ParseTeamInfo(); return; } if ( !strcmp( cmd, "map_restart" ) ) { CG_MapRestart(); return; } if ( Q_stricmp (cmd, "remapShader") == 0 ) { if (trap_Argc() == 4) { char shader1[MAX_QPATH]; char shader2[MAX_QPATH]; char shader3[MAX_QPATH]; Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1)); Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2)); Q_strncpyz(shader3, CG_Argv(3), sizeof(shader3)); trap_R_RemapShader(shader1, shader2, shader3); } return; } // loaddeferred can be both a servercmd and a consolecmd if ( !strcmp( cmd, "loaddefered" ) ) { // FIXME: spelled wrong, but not changing for demo CG_LoadDeferredPlayers(); return; } // clientLevelShot is sent before taking a special screenshot for // the menu system during development if ( !strcmp( cmd, "clientLevelShot" ) ) { cg.levelShot = qtrue; return; } // challenge completed is determened by the server. A client should consider this message valid: if ( !strcmp( cmd, "ch" ) ) { CG_ParseChallenge(); return; } if ( !strcmp (cmd, "oh") ) { CG_ParseObeliskHealth(); return; } if ( !strcmp( cmd, "respawn" ) ) { CG_ParseRespawnTime(); return; } if ( !strcmp( cmd, "team" ) ) { CG_ParseTeam(); return; } if ( !strcmp( cmd, "customvotes" ) ) { char infoString[1024]; int i; //TODO: Create a ParseCustomvotes function memset(&infoString,0,sizeof(infoString)); for(i=1;i<=12;i++) { Q_strcat(infoString,sizeof(infoString),CG_Argv( i )); Q_strcat(infoString,sizeof(infoString)," "); } trap_Cvar_Set("cg_vote_custom_commands",infoString); return; } CG_Printf( "Unknown client game command: %s\n", cmd ); } /* ==================== CG_ExecuteNewServerCommands Execute all of the server commands that were received along with this this snapshot. ==================== */ void CG_ExecuteNewServerCommands( int latestSequence ) { while ( cgs.serverCommandSequence < latestSequence ) { if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) { CG_ServerCommand(); } } } openarena_0.8.8.orig/code/cgame/cg_challenges.c0000644000175000017500000001025311656310265020140 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2008 Poul Sander This file is part of Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //#include "cg_local.h" #include "../qcommon/q_shared.h" #include "../renderer/tr_types.h" #include "../game/bg_public.h" #include "../game/challenges.h" #ifdef PARTofUI #include "../q3_ui/ui_local.h" #else #include "../cgame/cg_local.h" #endif #define FILENAME "challenges.dat" //First two static variables that may only be accessed throgh functions in this file //The challengeTable is of constant size so there is room for more challenges being added in the furture static unsigned int challengeTable[CHALLENGES_MAX]; //We have a variable to see if the system is initialized static qboolean challengesInitialized = qfalse; //This function initializes the challenge system. It has no effect if the system is already intialized. void challenges_init(void) { fileHandle_t file; int fileLen; int i; //If already initialized then do nothing if(challengesInitialized) return; //Else open file fileLen = trap_FS_FOpenFile(FILENAME, &file, FS_READ); //If the file not is big enough to contain all challenges then initialize from zero if(fileLen=CHALLENGES_MAX) return 0; return challengeTable[challenge]; } void addChallenge(int challenge) { if(challenge>=CHALLENGES_MAX) { #ifndef PARTofUI CG_Printf("Challenge #%u is >=CHALLENGES_MAX\n",challenge); #endif return; //Maybe also print an error? } challenges_init(); challengeTable[challenge]++; //CG_Printf("Increased challenge #%u by one. Is now %u\n",challenge,challengeTable[challenge]); } openarena_0.8.8.orig/code/cgame/cg_syscalls.c0000644000175000017500000003317511656310265017700 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_syscalls.c -- this file is only included when building a dll // cg_syscalls.asm is included instead when building a qvm #ifdef Q3_VM #error "Do not use in VM build" #endif #include "cg_local.h" static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1; void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) { syscall = syscallptr; } int PASSFLOAT( float x ) { float floatTemp; floatTemp = x; return *(int *)&floatTemp; } void trap_Print( const char *fmt ) { syscall( CG_PRINT, fmt ); } void trap_Error( const char *fmt ) { syscall( CG_ERROR, fmt ); exit(CG_ERROR); //Will never occour but makes compiler happy } int trap_Milliseconds( void ) { return syscall( CG_MILLISECONDS ); } void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags ); } void trap_Cvar_Update( vmCvar_t *vmCvar ) { syscall( CG_CVAR_UPDATE, vmCvar ); } void trap_Cvar_Set( const char *var_name, const char *value ) { syscall( CG_CVAR_SET, var_name, value ); } void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize ); } int trap_Argc( void ) { return syscall( CG_ARGC ); } void trap_Argv( int n, char *buffer, int bufferLength ) { syscall( CG_ARGV, n, buffer, bufferLength ); } void trap_Args( char *buffer, int bufferLength ) { syscall( CG_ARGS, buffer, bufferLength ); } int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { return syscall( CG_FS_FOPENFILE, qpath, f, mode ); } void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { syscall( CG_FS_READ, buffer, len, f ); } void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { syscall( CG_FS_WRITE, buffer, len, f ); } void trap_FS_FCloseFile( fileHandle_t f ) { syscall( CG_FS_FCLOSEFILE, f ); } int trap_FS_Seek( fileHandle_t f, long offset, int origin ) { return syscall( CG_FS_SEEK, f, offset, origin ); } void trap_SendConsoleCommand( const char *text ) { syscall( CG_SENDCONSOLECOMMAND, text ); } void trap_AddCommand( const char *cmdName ) { syscall( CG_ADDCOMMAND, cmdName ); } void trap_RemoveCommand( const char *cmdName ) { syscall( CG_REMOVECOMMAND, cmdName ); } void trap_SendClientCommand( const char *s ) { syscall( CG_SENDCLIENTCOMMAND, s ); } void trap_UpdateScreen( void ) { syscall( CG_UPDATESCREEN ); } void trap_CM_LoadMap( const char *mapname ) { syscall( CG_CM_LOADMAP, mapname ); } int trap_CM_NumInlineModels( void ) { return syscall( CG_CM_NUMINLINEMODELS ); } clipHandle_t trap_CM_InlineModel( int index ) { return syscall( CG_CM_INLINEMODEL, index ); } clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) { return syscall( CG_CM_TEMPBOXMODEL, mins, maxs ); } clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs ) { return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs ); } int trap_CM_PointContents( const vec3_t p, clipHandle_t model ) { return syscall( CG_CM_POINTCONTENTS, p, model ); } int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) { return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles ); } void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ) { syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask ); } void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ) { syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask ); } void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ) { syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); } void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ) { syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); } int trap_CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer ); } void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) { syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx ); } void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) { syscall( CG_S_STARTLOCALSOUND, sfx, channelNum ); } void trap_S_ClearLoopingSounds( qboolean killall ) { syscall( CG_S_CLEARLOOPINGSOUNDS, killall ); } void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx ); } void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { syscall( CG_S_ADDREALLOOPINGSOUND, entityNum, origin, velocity, sfx ); } void trap_S_StopLoopingSound( int entityNum ) { syscall( CG_S_STOPLOOPINGSOUND, entityNum ); } void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin ); } void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) { syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater ); } sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { return syscall( CG_S_REGISTERSOUND, sample, compressed ); } void trap_S_StartBackgroundTrack( const char *intro, const char *loop ) { syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop ); } void trap_R_LoadWorldMap( const char *mapname ) { syscall( CG_R_LOADWORLDMAP, mapname ); } qhandle_t trap_R_RegisterModel( const char *name ) { return syscall( CG_R_REGISTERMODEL, name ); } qhandle_t trap_R_RegisterSkin( const char *name ) { return syscall( CG_R_REGISTERSKIN, name ); } qhandle_t trap_R_RegisterShader( const char *name ) { return syscall( CG_R_REGISTERSHADER, name ); } qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { return syscall( CG_R_REGISTERSHADERNOMIP, name ); } void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { syscall(CG_R_REGISTERFONT, fontName, pointSize, font ); } void trap_R_ClearScene( void ) { syscall( CG_R_CLEARSCENE ); } void trap_R_AddRefEntityToScene( const refEntity_t *re ) { syscall( CG_R_ADDREFENTITYTOSCENE, re ); } void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) { syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts ); } void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ) { syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num ); } int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) { return syscall( CG_R_LIGHTFORPOINT, point, ambientLight, directedLight, lightDir ); } void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); } void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { syscall( CG_R_ADDADDITIVELIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); } void trap_R_RenderScene( const refdef_t *fd ) { syscall( CG_R_RENDERSCENE, fd ); } void trap_R_SetColor( const float *rgba ) { syscall( CG_R_SETCOLOR, rgba ); } void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) { syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader ); } void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { syscall( CG_R_MODELBOUNDS, model, mins, maxs ); } int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ) { return syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName ); } void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset ); } void trap_GetGlconfig( glconfig_t *glconfig ) { syscall( CG_GETGLCONFIG, glconfig ); } void trap_GetGameState( gameState_t *gamestate ) { syscall( CG_GETGAMESTATE, gamestate ); } void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime ); } qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); } qboolean trap_GetServerCommand( int serverCommandNumber ) { return syscall( CG_GETSERVERCOMMAND, serverCommandNumber ); } int trap_GetCurrentCmdNumber( void ) { return syscall( CG_GETCURRENTCMDNUMBER ); } qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { return syscall( CG_GETUSERCMD, cmdNumber, ucmd ); } void trap_SetUserCmdValue( int stateValue, float sensitivityScale ) { syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale) ); } void testPrintInt( char *string, int i ) { syscall( CG_TESTPRINTINT, string, i ); } void testPrintFloat( char *string, float f ) { syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT(f) ); } int trap_MemoryRemaining( void ) { return syscall( CG_MEMORY_REMAINING ); } qboolean trap_Key_IsDown( int keynum ) { return syscall( CG_KEY_ISDOWN, keynum ); } int trap_Key_GetCatcher( void ) { return syscall( CG_KEY_GETCATCHER ); } void trap_Key_SetCatcher( int catcher ) { syscall( CG_KEY_SETCATCHER, catcher ); } int trap_Key_GetKey( const char *binding ) { return syscall( CG_KEY_GETKEY, binding ); } int trap_PC_AddGlobalDefine( char *define ) { return syscall( CG_PC_ADD_GLOBAL_DEFINE, define ); } int trap_PC_LoadSource( const char *filename ) { return syscall( CG_PC_LOAD_SOURCE, filename ); } int trap_PC_FreeSource( int handle ) { return syscall( CG_PC_FREE_SOURCE, handle ); } int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { return syscall( CG_PC_READ_TOKEN, handle, pc_token ); } int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { return syscall( CG_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); } void trap_S_StopBackgroundTrack( void ) { syscall( CG_S_STOPBACKGROUNDTRACK ); } int trap_RealTime(qtime_t *qtime) { return syscall( CG_REAL_TIME, qtime ); } void trap_SnapVector( float *v ) { syscall( CG_SNAPVECTOR, v ); } // this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate) int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) { return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits); } // stops playing the cinematic and ends it. should always return FMV_EOF // cinematics must be stopped in reverse order of when they are started e_status trap_CIN_StopCinematic(int handle) { return syscall(CG_CIN_STOPCINEMATIC, handle); } // will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached. e_status trap_CIN_RunCinematic (int handle) { return syscall(CG_CIN_RUNCINEMATIC, handle); } // draws the current frame void trap_CIN_DrawCinematic (int handle) { syscall(CG_CIN_DRAWCINEMATIC, handle); } // allows you to resize the animation dynamically void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) { syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h); } /* qboolean trap_loadCamera( const char *name ) { return syscall( CG_LOADCAMERA, name ); } void trap_startCamera(int time) { syscall(CG_STARTCAMERA, time); } qboolean trap_getCameraInfo( int time, vec3_t *origin, vec3_t *angles) { return syscall( CG_GETCAMERAINFO, time, origin, angles ); } */ qboolean trap_GetEntityToken( char *buffer, int bufferSize ) { return syscall( CG_GET_ENTITY_TOKEN, buffer, bufferSize ); } qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ) { return syscall( CG_R_INPVS, p1, p2 ); } openarena_0.8.8.orig/code/cgame/cg_consolecmds.c0000644000175000017500000003625111656310265020352 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_consolecmds.c -- text commands typed in at the local console, or // executed by a key binding #include "cg_local.h" #include "../ui/ui_shared.h" #ifdef MISSIONPACK extern menuDef_t *menuScoreboard; #endif void CG_PrintClientNumbers( void ) { int i; CG_Printf( "slot score ping name\n" ); CG_Printf( "---- ----- ---- ----\n" ); for(i=0;ips.clientNum; if (ci) { if (!ci->teamLeader && sortedTeamPlayers[cg_currentSelectedPlayer.integer] != cg.snap->ps.clientNum) { return; } } if (cgs.currentOrder < TEAMTASK_CAMP) { cgs.currentOrder++; if (cgs.currentOrder == TEAMTASK_RETRIEVE) { if (!CG_OtherTeamHasFlag()) { cgs.currentOrder++; } } if (cgs.currentOrder == TEAMTASK_ESCORT) { if (!CG_YourTeamHasFlag()) { cgs.currentOrder++; } } } else { cgs.currentOrder = TEAMTASK_OFFENSE; } cgs.orderPending = qtrue; cgs.orderTime = cg.time + 3000; } static void CG_ConfirmOrder_f (void ) { trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_YES)); trap_SendConsoleCommand("+button5; wait; -button5"); if (cg.time < cgs.acceptOrderTime) { trap_SendClientCommand(va("teamtask %d\n", cgs.acceptTask)); cgs.acceptOrderTime = 0; } } static void CG_DenyOrder_f (void ) { trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_NO)); trap_SendConsoleCommand("+button6; wait; -button6"); if (cg.time < cgs.acceptOrderTime) { cgs.acceptOrderTime = 0; } } static void CG_TaskOffense_f (void ) { if (cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_1FCTF) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONGETFLAG)); } else { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONOFFENSE)); } trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_OFFENSE)); } static void CG_TaskDefense_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONDEFENSE)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_DEFENSE)); } static void CG_TaskPatrol_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONPATROL)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_PATROL)); } static void CG_TaskCamp_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONCAMPING)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_CAMP)); } static void CG_TaskFollow_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOW)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_FOLLOW)); } static void CG_TaskRetrieve_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONRETURNFLAG)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_RETRIEVE)); } static void CG_TaskEscort_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOWCARRIER)); trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_ESCORT)); } static void CG_TaskOwnFlag_f (void ) { trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_IHAVEFLAG)); } static void CG_TauntKillInsult_f (void ) { trap_SendConsoleCommand("cmd vsay kill_insult\n"); } static void CG_TauntPraise_f (void ) { trap_SendConsoleCommand("cmd vsay praise\n"); } static void CG_TauntTaunt_f (void ) { trap_SendConsoleCommand("cmd vtaunt\n"); } static void CG_TauntDeathInsult_f (void ) { trap_SendConsoleCommand("cmd vsay death_insult\n"); } static void CG_TauntGauntlet_f (void ) { trap_SendConsoleCommand("cmd vsay kill_guantlet\n"); } static void CG_TaskSuicide_f (void ) { int clientNum; char command[128]; clientNum = CG_CrosshairPlayer(); if ( clientNum == -1 ) { return; } Com_sprintf( command, 128, "tell %i suicide", clientNum ); trap_SendClientCommand( command ); } /* ================== CG_TeamMenu_f ================== */ /* static void CG_TeamMenu_f( void ) { if (trap_Key_GetCatcher() & KEYCATCH_CGAME) { CG_EventHandling(CGAME_EVENT_NONE); trap_Key_SetCatcher(0); } else { CG_EventHandling(CGAME_EVENT_TEAMMENU); //trap_Key_SetCatcher(KEYCATCH_CGAME); } } */ /* ================== CG_EditHud_f ================== */ /* static void CG_EditHud_f( void ) { //cls.keyCatchers ^= KEYCATCH_CGAME; //VM_Call (cgvm, CG_EVENT_HANDLING, (cls.keyCatchers & KEYCATCH_CGAME) ? CGAME_EVENT_EDITHUD : CGAME_EVENT_NONE); } */ #endif /* ================== CG_StartOrbit_f ================== */ static void CG_StartOrbit_f( void ) { char var[MAX_TOKEN_CHARS]; trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) ); if ( !atoi(var) ) { return; } if (cg_cameraOrbit.value != 0) { trap_Cvar_Set ("cg_cameraOrbit", "0"); trap_Cvar_Set("cg_thirdPerson", "0"); } else { trap_Cvar_Set("cg_cameraOrbit", "5"); trap_Cvar_Set("cg_thirdPerson", "1"); trap_Cvar_Set("cg_thirdPersonAngle", "0"); trap_Cvar_Set("cg_thirdPersonRange", "100"); } } /* static void CG_Camera_f( void ) { char name[1024]; trap_Argv( 1, name, sizeof(name)); if (trap_loadCamera(name)) { cg.cameraMode = qtrue; trap_startCamera(cg.time); } else { CG_Printf ("Unable to load camera %s\n",name); } } */ typedef struct { char *cmd; void (*function)(void); } consoleCommand_t; static consoleCommand_t commands[] = { { "testgun", CG_TestGun_f }, { "testmodel", CG_TestModel_f }, { "nextframe", CG_TestModelNextFrame_f }, { "prevframe", CG_TestModelPrevFrame_f }, { "nextskin", CG_TestModelNextSkin_f }, { "prevskin", CG_TestModelPrevSkin_f }, { "viewpos", CG_Viewpos_f }, { "+scores", CG_ScoresDown_f }, { "-scores", CG_ScoresUp_f }, { "+zoom", CG_ZoomDown_f }, { "-zoom", CG_ZoomUp_f }, { "sizeup", CG_SizeUp_f }, { "sizedown", CG_SizeDown_f }, { "weapnext", CG_NextWeapon_f }, { "weapprev", CG_PrevWeapon_f }, { "weapon", CG_Weapon_f }, { "tell_target", CG_TellTarget_f }, { "tell_attacker", CG_TellAttacker_f }, { "vtell_target", CG_VoiceTellTarget_f }, { "vtell_attacker", CG_VoiceTellAttacker_f }, { "tcmd", CG_TargetCommand_f }, #ifdef MISSIONPACK { "loadhud", CG_LoadHud_f }, { "nextTeamMember", CG_NextTeamMember_f }, { "prevTeamMember", CG_PrevTeamMember_f }, { "nextOrder", CG_NextOrder_f }, { "confirmOrder", CG_ConfirmOrder_f }, { "denyOrder", CG_DenyOrder_f }, { "taskOffense", CG_TaskOffense_f }, { "taskDefense", CG_TaskDefense_f }, { "taskPatrol", CG_TaskPatrol_f }, { "taskCamp", CG_TaskCamp_f }, { "taskFollow", CG_TaskFollow_f }, { "taskRetrieve", CG_TaskRetrieve_f }, { "taskEscort", CG_TaskEscort_f }, { "taskSuicide", CG_TaskSuicide_f }, { "taskOwnFlag", CG_TaskOwnFlag_f }, { "tauntKillInsult", CG_TauntKillInsult_f }, { "tauntPraise", CG_TauntPraise_f }, { "tauntTaunt", CG_TauntTaunt_f }, { "tauntDeathInsult", CG_TauntDeathInsult_f }, { "tauntGauntlet", CG_TauntGauntlet_f }, { "spWin", CG_spWin_f }, { "spLose", CG_spLose_f }, { "scoresDown", CG_scrollScoresDown_f }, { "scoresUp", CG_scrollScoresUp_f }, #endif { "startOrbit", CG_StartOrbit_f }, // { "camera", CG_Camera_f }, { "loaddeferred", CG_LoadDeferredPlayers }, { "+acc", CG_AccDown_f }, { "-acc", CG_AccUp_f }, { "clients", CG_PrintClientNumbers } }; /* ================= CG_ConsoleCommand The string has been tokenized and can be retrieved with Cmd_Argc() / Cmd_Argv() ================= */ qboolean CG_ConsoleCommand( void ) { const char *cmd; int i; cmd = CG_Argv(0); for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { if ( !Q_stricmp( cmd, commands[i].cmd ) ) { commands[i].function(); return qtrue; } } return qfalse; } /* ================= CG_InitConsoleCommands Let the client system know about all of our commands so it can perform tab completion ================= */ void CG_InitConsoleCommands( void ) { int i; for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { trap_AddCommand( commands[i].cmd ); } // // the game server will interpret these commands, which will be automatically // forwarded to the server after they are not recognized locally // trap_AddCommand ("kill"); trap_AddCommand ("say"); trap_AddCommand ("say_team"); trap_AddCommand ("tell"); trap_AddCommand ("vsay"); trap_AddCommand ("vsay_team"); trap_AddCommand ("vtell"); trap_AddCommand ("vtaunt"); trap_AddCommand ("vosay"); trap_AddCommand ("vosay_team"); trap_AddCommand ("votell"); trap_AddCommand ("give"); trap_AddCommand ("god"); trap_AddCommand ("notarget"); trap_AddCommand ("noclip"); trap_AddCommand ("team"); trap_AddCommand ("follow"); trap_AddCommand ("levelshot"); trap_AddCommand ("addbot"); trap_AddCommand ("setviewpos"); trap_AddCommand ("callvote"); trap_AddCommand ("getmappage"); trap_AddCommand ("vote"); trap_AddCommand ("callteamvote"); trap_AddCommand ("teamvote"); trap_AddCommand ("stats"); trap_AddCommand ("teamtask"); trap_AddCommand ("loaddefered"); // spelled wrong, but not changing for demo } openarena_0.8.8.orig/code/cgame/cg_main.c0000644000175000017500000024130611656310265016764 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_main.c -- initialization and primary entry point for cgame #include "cg_local.h" #ifdef MISSIONPACK #include "../ui/ui_shared.h" // display context for new ui stuff displayContextDef_t cgDC; #endif int forceModelModificationCount = -1; void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ); void CG_Shutdown( void ); /* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case CG_INIT: CG_Init( arg0, arg1, arg2 ); return 0; case CG_SHUTDOWN: CG_Shutdown(); return 0; case CG_CONSOLE_COMMAND: return CG_ConsoleCommand(); case CG_DRAW_ACTIVE_FRAME: CG_DrawActiveFrame( arg0, arg1, arg2 ); CG_FairCvars(); return 0; case CG_CROSSHAIR_PLAYER: return CG_CrosshairPlayer(); case CG_LAST_ATTACKER: return CG_LastAttacker(); case CG_KEY_EVENT: CG_KeyEvent(arg0, arg1); return 0; case CG_MOUSE_EVENT: #ifdef MISSIONPACK cgDC.cursorx = cgs.cursorX; cgDC.cursory = cgs.cursorY; #endif CG_MouseEvent(arg0, arg1); return 0; case CG_EVENT_HANDLING: CG_EventHandling(arg0); return 0; default: CG_Error( "vmMain: unknown command %i", command ); break; } return -1; } cg_t cg; cgs_t cgs; centity_t cg_entities[MAX_GENTITIES]; weaponInfo_t cg_weapons[MAX_WEAPONS]; itemInfo_t cg_items[MAX_ITEMS]; vmCvar_t cg_railTrailTime; vmCvar_t cg_centertime; vmCvar_t cg_runpitch; vmCvar_t cg_runroll; vmCvar_t cg_bobup; vmCvar_t cg_bobpitch; vmCvar_t cg_bobroll; vmCvar_t cg_swingSpeed; vmCvar_t cg_shadows; vmCvar_t cg_gibs; vmCvar_t cg_drawTimer; vmCvar_t cg_drawFPS; vmCvar_t cg_drawSnapshot; vmCvar_t cg_draw3dIcons; vmCvar_t cg_drawIcons; vmCvar_t cg_drawAmmoWarning; vmCvar_t cg_drawCrosshair; vmCvar_t cg_drawCrosshairNames; vmCvar_t cg_drawRewards; vmCvar_t cg_crosshairSize; vmCvar_t cg_crosshairX; vmCvar_t cg_crosshairY; vmCvar_t cg_crosshairHealth; vmCvar_t cg_draw2D; vmCvar_t cg_drawStatus; vmCvar_t cg_animSpeed; vmCvar_t cg_debugAnim; vmCvar_t cg_debugPosition; vmCvar_t cg_debugEvents; vmCvar_t cg_errorDecay; vmCvar_t cg_nopredict; vmCvar_t cg_noPlayerAnims; vmCvar_t cg_showmiss; vmCvar_t cg_footsteps; vmCvar_t cg_addMarks; vmCvar_t cg_brassTime; vmCvar_t cg_viewsize; vmCvar_t cg_drawGun; vmCvar_t cg_gun_frame; vmCvar_t cg_gun_x; vmCvar_t cg_gun_y; vmCvar_t cg_gun_z; vmCvar_t cg_tracerChance; vmCvar_t cg_tracerWidth; vmCvar_t cg_tracerLength; vmCvar_t cg_autoswitch; vmCvar_t cg_ignore; vmCvar_t cg_simpleItems; vmCvar_t cg_fov; vmCvar_t cg_zoomFov; vmCvar_t cg_thirdPerson; vmCvar_t cg_thirdPersonRange; vmCvar_t cg_thirdPersonAngle; vmCvar_t cg_lagometer; vmCvar_t cg_drawAttacker; vmCvar_t cg_drawSpeed; vmCvar_t cg_synchronousClients; vmCvar_t cg_teamChatTime; vmCvar_t cg_teamChatHeight; vmCvar_t cg_stats; vmCvar_t cg_buildScript; vmCvar_t cg_forceModel; vmCvar_t cg_paused; vmCvar_t cg_blood; vmCvar_t cg_predictItems; vmCvar_t cg_deferPlayers; vmCvar_t cg_drawTeamOverlay; vmCvar_t cg_teamOverlayUserinfo; vmCvar_t cg_drawFriend; vmCvar_t cg_teamChatsOnly; vmCvar_t cg_noVoiceChats; vmCvar_t cg_noVoiceText; vmCvar_t cg_hudFiles; vmCvar_t cg_scorePlum; //unlagged - smooth clients #2 // this is done server-side now //vmCvar_t cg_smoothClients; //unlagged - smooth clients #2 vmCvar_t pmove_fixed; //vmCvar_t cg_pmove_fixed; vmCvar_t pmove_msec; vmCvar_t pmove_float; vmCvar_t cg_pmove_msec; vmCvar_t cg_cameraMode; vmCvar_t cg_cameraOrbit; vmCvar_t cg_cameraOrbitDelay; vmCvar_t cg_timescaleFadeEnd; vmCvar_t cg_timescaleFadeSpeed; vmCvar_t cg_timescale; vmCvar_t cg_smallFont; vmCvar_t cg_bigFont; vmCvar_t cg_noTaunt; vmCvar_t cg_noProjectileTrail; vmCvar_t cg_oldRail; vmCvar_t cg_oldRocket; vmCvar_t cg_leiEnhancement; // ANOTHER LEILEI LINE!!! vmCvar_t cg_leiBrassNoise; // ANOTHER LEILEI LINE!!! vmCvar_t cg_leiGoreNoise; // ANOTHER LEILEI LINE!!! vmCvar_t cg_leiSuperGoreyAwesome; // ANOTHER LEILEI LINE!!! vmCvar_t cg_oldPlasma; vmCvar_t cg_trueLightning; vmCvar_t cg_music; vmCvar_t cg_weaponOrder; #ifdef MISSIONPACK vmCvar_t cg_redTeamName; vmCvar_t cg_blueTeamName; vmCvar_t cg_currentSelectedPlayer; vmCvar_t cg_currentSelectedPlayerName; vmCvar_t cg_singlePlayer; vmCvar_t cg_singlePlayerActive; vmCvar_t cg_recordSPDemo; vmCvar_t cg_recordSPDemoName; #endif vmCvar_t cg_obeliskRespawnDelay; vmCvar_t cg_enableDust; vmCvar_t cg_enableBreath; //unlagged - client options vmCvar_t cg_delag; //vmCvar_t cg_debugDelag; //vmCvar_t cg_drawBBox; vmCvar_t cg_cmdTimeNudge; vmCvar_t sv_fps; vmCvar_t cg_projectileNudge; vmCvar_t cg_optimizePrediction; vmCvar_t cl_timeNudge; //vmCvar_t cg_latentSnaps; //vmCvar_t cg_latentCmds; //vmCvar_t cg_plOut; //unlagged - client options //elimination addition vmCvar_t cg_alwaysWeaponBar; vmCvar_t cg_hitsound; vmCvar_t cg_voip_teamonly; vmCvar_t cg_voteflags; vmCvar_t cg_cyclegrapple; vmCvar_t cg_vote_custom_commands; vmCvar_t cg_autovertex; vmCvar_t cg_fragmsgsize; vmCvar_t cg_crosshairPulse; vmCvar_t cg_differentCrosshairs; vmCvar_t cg_ch1; vmCvar_t cg_ch1size; vmCvar_t cg_ch2; vmCvar_t cg_ch2size; vmCvar_t cg_ch3; vmCvar_t cg_ch3size; vmCvar_t cg_ch4; vmCvar_t cg_ch4size; vmCvar_t cg_ch5; vmCvar_t cg_ch5size; vmCvar_t cg_ch6; vmCvar_t cg_ch6size; vmCvar_t cg_ch7; vmCvar_t cg_ch7size; vmCvar_t cg_ch8; vmCvar_t cg_ch8size; vmCvar_t cg_ch9; vmCvar_t cg_ch9size; vmCvar_t cg_ch10; vmCvar_t cg_ch10size; vmCvar_t cg_ch11; vmCvar_t cg_ch11size; vmCvar_t cg_ch12; vmCvar_t cg_ch12size; vmCvar_t cg_ch13; vmCvar_t cg_ch13size; vmCvar_t cg_crosshairColorRed; vmCvar_t cg_crosshairColorGreen; vmCvar_t cg_crosshairColorBlue; vmCvar_t cg_weaponBarStyle; vmCvar_t cg_chatBeep; vmCvar_t cg_teamChatBeep; typedef struct { vmCvar_t *vmCvar; char *cvarName; char *defaultString; int cvarFlags; } cvarTable_t; static cvarTable_t cvarTable[] = { // bk001129 { &cg_ignore, "cg_ignore", "0", 0 }, // used for debugging { &cg_autoswitch, "cg_autoswitch", "1", CVAR_ARCHIVE }, { &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE }, { &cg_zoomFov, "cg_zoomfov", "22.5", CVAR_ARCHIVE }, { &cg_fov, "cg_fov", "90", CVAR_ARCHIVE }, { &cg_viewsize, "cg_viewsize", "100", CVAR_ARCHIVE }, { &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE }, { &cg_gibs, "cg_gibs", "1", CVAR_ARCHIVE }, { &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE }, { &cg_drawStatus, "cg_drawStatus", "1", CVAR_ARCHIVE }, { &cg_drawTimer, "cg_drawTimer", "0", CVAR_ARCHIVE }, { &cg_drawFPS, "cg_drawFPS", "0", CVAR_ARCHIVE }, { &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE }, { &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE }, { &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE }, { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE }, { &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE }, { &cg_drawSpeed, "cg_drawSpeed", "0", CVAR_ARCHIVE }, { &cg_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, { &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE }, { &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE }, { &cg_crosshairHealth, "cg_crosshairHealth", "1", CVAR_ARCHIVE }, { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE }, { &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE }, { &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, { &cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE }, { &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE }, { &cg_lagometer, "cg_lagometer", "1", CVAR_ARCHIVE }, { &cg_railTrailTime, "cg_railTrailTime", "600", CVAR_ARCHIVE }, { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT }, { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT }, { &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT }, { &cg_centertime, "cg_centertime", "3", CVAR_CHEAT }, { &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE}, { &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE }, { &cg_bobup , "cg_bobup", "0.005", CVAR_CHEAT }, { &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE }, { &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE }, { &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT }, { &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT }, { &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT }, { &cg_debugPosition, "cg_debugposition", "0", CVAR_CHEAT }, { &cg_debugEvents, "cg_debugevents", "0", CVAR_CHEAT }, { &cg_errorDecay, "cg_errordecay", "100", 0 }, { &cg_nopredict, "cg_nopredict", "0", 0 }, { &cg_noPlayerAnims, "cg_noplayeranims", "0", CVAR_CHEAT }, { &cg_showmiss, "cg_showmiss", "0", 0 }, { &cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT }, { &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT }, { &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT }, { &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT }, { &cg_thirdPersonRange, "cg_thirdPersonRange", "40", CVAR_CHEAT }, { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT }, { &cg_thirdPerson, "cg_thirdPerson", "0", 0 }, { &cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE }, { &cg_teamChatHeight, "cg_teamChatHeight", "0", CVAR_ARCHIVE }, { &cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE }, { &cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE }, #ifdef MISSIONPACK { &cg_deferPlayers, "cg_deferPlayers", "0", CVAR_ARCHIVE }, #else { &cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE }, #endif { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE }, { &cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO }, { &cg_stats, "cg_stats", "0", 0 }, { &cg_drawFriend, "cg_drawFriend", "1", CVAR_ARCHIVE }, { &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE }, { &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE }, { &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE }, // the following variables are created in other parts of the system, // but we also reference them here { &cg_buildScript, "com_buildScript", "0", 0 }, // force loading of all possible data amd error on failures { &cg_paused, "cl_paused", "0", CVAR_ROM }, { &cg_blood, "com_blood", "1", CVAR_ARCHIVE }, { &cg_alwaysWeaponBar, "cg_alwaysWeaponBar", "0", CVAR_ARCHIVE}, //Elimination { &cg_hitsound, "cg_hitsound", "0", CVAR_ARCHIVE}, { &cg_voip_teamonly, "cg_voipTeamOnly", "1", CVAR_ARCHIVE}, { &cg_voteflags, "cg_voteflags", "*", CVAR_ROM}, { &cg_cyclegrapple, "cg_cyclegrapple", "1", CVAR_ARCHIVE}, { &cg_vote_custom_commands, "cg_vote_custom_commands", "", CVAR_ROM }, { &cg_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO }, // communicated by systeminfo { &cg_autovertex, "cg_autovertex", "0", CVAR_ARCHIVE }, #ifdef MISSIONPACK { &cg_redTeamName, "g_redteam", DEFAULT_REDTEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, { &cg_blueTeamName, "g_blueteam", DEFAULT_BLUETEAM_NAME, CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO }, { &cg_currentSelectedPlayer, "cg_currentSelectedPlayer", "0", CVAR_ARCHIVE}, { &cg_currentSelectedPlayerName, "cg_currentSelectedPlayerName", "", CVAR_ARCHIVE}, { &cg_singlePlayer, "ui_singlePlayerActive", "0", CVAR_USERINFO}, { &cg_singlePlayerActive, "ui_singlePlayerActive", "0", CVAR_USERINFO}, { &cg_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE}, { &cg_recordSPDemoName, "ui_recordSPDemoName", "", CVAR_ARCHIVE}, { &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE}, #endif { &cg_enableDust, "g_enableDust", "0", CVAR_SERVERINFO}, { &cg_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO}, { &cg_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO}, { &cg_cameraOrbit, "cg_cameraOrbit", "0", CVAR_CHEAT}, { &cg_cameraOrbitDelay, "cg_cameraOrbitDelay", "50", CVAR_ARCHIVE}, { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", 0}, { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0}, { &cg_timescale, "timescale", "1", 0}, { &cg_scorePlum, "cg_scorePlums", "1", CVAR_USERINFO | CVAR_ARCHIVE}, //unlagged - smooth clients #2 // this is done server-side now // { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, //unlagged - smooth clients #2 { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO}, { &pmove_msec, "pmove_msec", "11", CVAR_SYSTEMINFO}, { &pmove_float, "pmove_float", "1", CVAR_SYSTEMINFO}, { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE}, { &cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, { &cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, { &cg_oldRail, "cg_oldRail", "0", CVAR_ARCHIVE}, { &cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE}, { &cg_leiEnhancement, "cg_leiEnhancement", "0", CVAR_ARCHIVE}, // LEILEI default off (in case of whiner) { &cg_leiGoreNoise, "cg_leiGoreNoise", "0", CVAR_ARCHIVE}, // LEILEI { &cg_leiBrassNoise, "cg_leiBrassNoise", "0", CVAR_ARCHIVE}, // LEILEI { &cg_leiSuperGoreyAwesome, "cg_leiSuperGoreyAwesome", "0", CVAR_ARCHIVE}, // LEILEI { &cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE}, //unlagged - client options { &cg_delag, "cg_delag", "1", CVAR_ARCHIVE | CVAR_USERINFO }, // { &cg_debugDelag, "cg_debugDelag", "0", CVAR_USERINFO | CVAR_CHEAT }, // { &cg_drawBBox, "cg_drawBBox", "0", CVAR_CHEAT }, { &cg_cmdTimeNudge, "cg_cmdTimeNudge", "0", CVAR_ARCHIVE | CVAR_USERINFO }, // this will be automagically copied from the server { &sv_fps, "sv_fps", "20", CVAR_SYSTEMINFO }, { &cg_projectileNudge, "cg_projectileNudge", "0", CVAR_ARCHIVE }, { &cg_optimizePrediction, "cg_optimizePrediction", "1", CVAR_ARCHIVE }, { &cl_timeNudge, "cl_timeNudge", "0", CVAR_ARCHIVE }, // { &cg_latentSnaps, "cg_latentSnaps", "0", CVAR_USERINFO | CVAR_CHEAT }, // { &cg_latentCmds, "cg_latentCmds", "0", CVAR_USERINFO | CVAR_CHEAT }, // { &cg_plOut, "cg_plOut", "0", CVAR_USERINFO | CVAR_CHEAT }, //unlagged - client options { &cg_trueLightning, "cg_trueLightning", "0.0", CVAR_ARCHIVE}, { &cg_music, "cg_music", "", CVAR_ARCHIVE}, // { &cg_pmove_fixed, "cg_pmove_fixed", "0", CVAR_USERINFO | CVAR_ARCHIVE } { &cg_fragmsgsize, "cg_fragmsgsize", "1.0", CVAR_ARCHIVE}, { &cg_crosshairPulse, "cg_crosshairPulse", "1", CVAR_ARCHIVE}, { &cg_differentCrosshairs, "cg_differentCrosshairs", "0", CVAR_ARCHIVE}, { &cg_ch1, "cg_ch1", "1", CVAR_ARCHIVE}, { &cg_ch1size, "cg_ch1size", "24", CVAR_ARCHIVE}, { &cg_ch2, "cg_ch2", "1", CVAR_ARCHIVE}, { &cg_ch2size, "cg_ch2size", "24", CVAR_ARCHIVE}, { &cg_ch3, "cg_ch3", "1", CVAR_ARCHIVE}, { &cg_ch3size, "cg_ch3size", "24", CVAR_ARCHIVE}, { &cg_ch4, "cg_ch4", "1", CVAR_ARCHIVE}, { &cg_ch4size, "cg_ch4size", "24", CVAR_ARCHIVE}, { &cg_ch5, "cg_ch5", "1", CVAR_ARCHIVE}, { &cg_ch5size, "cg_ch5size", "24", CVAR_ARCHIVE}, { &cg_ch6, "cg_ch6", "1", CVAR_ARCHIVE}, { &cg_ch6size, "cg_ch6size", "24", CVAR_ARCHIVE}, { &cg_ch7, "cg_ch7", "1", CVAR_ARCHIVE}, { &cg_ch7size, "cg_ch7size", "24", CVAR_ARCHIVE}, { &cg_ch8, "cg_ch8", "1", CVAR_ARCHIVE}, { &cg_ch8size, "cg_ch8size", "24", CVAR_ARCHIVE}, { &cg_ch9, "cg_ch9", "1", CVAR_ARCHIVE}, { &cg_ch9size, "cg_ch9size", "24", CVAR_ARCHIVE}, { &cg_ch10, "cg_ch10", "1", CVAR_ARCHIVE}, { &cg_ch10size, "cg_ch10size", "24", CVAR_ARCHIVE}, { &cg_ch11, "cg_ch11", "1", CVAR_ARCHIVE}, { &cg_ch11size, "cg_ch11size", "24", CVAR_ARCHIVE}, { &cg_ch12, "cg_ch12", "1", CVAR_ARCHIVE}, { &cg_ch12size, "cg_ch12size", "24", CVAR_ARCHIVE}, { &cg_ch13, "cg_ch13", "1", CVAR_ARCHIVE}, { &cg_ch13size, "cg_ch13size", "24", CVAR_ARCHIVE}, { &cg_crosshairColorRed, "cg_crosshairColorRed", "1.0", CVAR_ARCHIVE}, { &cg_crosshairColorGreen, "cg_crosshairColorGreen", "1.0", CVAR_ARCHIVE}, { &cg_crosshairColorBlue, "cg_crosshairColorBlue", "1.0", CVAR_ARCHIVE}, { &cg_weaponBarStyle, "cg_weaponBarStyle", "0", CVAR_ARCHIVE}, { &cg_weaponOrder,"cg_weaponOrder", "/1/2/4/3/6/7/8/9/5/", CVAR_ARCHIVE}, {&cg_chatBeep, "cg_chatBeep", "1", CVAR_ARCHIVE }, {&cg_teamChatBeep, "cg_teamChatBeep", "1", CVAR_ARCHIVE } }; static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); /* ================= CG_RegisterCvars ================= */ void CG_RegisterCvars( void ) { int i; cvarTable_t *cv; char var[MAX_TOKEN_CHARS]; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); } // see if we are also running the server on this machine trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) ); cgs.localServer = atoi( var ); forceModelModificationCount = cg_forceModel.modificationCount; trap_Cvar_Register(NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE ); } /* =================== CG_ForceModelChange =================== */ static void CG_ForceModelChange( void ) { int i; for (i=0 ; ivmCvar == &cg_cmdTimeNudge ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, 0, 999 ); } // cl_timenudge less than -50 or greater than 50 doesn't actually // do anything more than -50 or 50 (actually the numbers are probably // closer to -30 and 30, but 50 is nice and round-ish) // might as well not feed the myth, eh? else if ( cv->vmCvar == &cl_timeNudge ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, -50, 50 ); } // don't let this go too high - no point /*else if ( cv->vmCvar == &cg_latentSnaps ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, 0, 10 ); }*/ // don't let this get too large /*else if ( cv->vmCvar == &cg_latentCmds ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, 0, MAX_LATENT_CMDS - 1 ); }*/ // no more than 100% packet loss /*else if ( cv->vmCvar == &cg_plOut ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, 0, 100 ); }*/ //unlagged - client options else if ( cv->vmCvar == &cg_errorDecay ) { CG_Cvar_ClampInt( cv->cvarName, cv->vmCvar, 0, 250 ); } trap_Cvar_Update( cv->vmCvar ); } // check for modications here // If team overlay is on, ask for updates from the server. If its off, // let the server know so we don't receive it if ( drawTeamOverlayModificationCount != cg_drawTeamOverlay.modificationCount ) { drawTeamOverlayModificationCount = cg_drawTeamOverlay.modificationCount; if ( cg_drawTeamOverlay.integer > 0 ) { trap_Cvar_Set( "teamoverlay", "1" ); } else { trap_Cvar_Set( "teamoverlay", "0" ); } } // if force model changed if ( forceModelModificationCount != cg_forceModel.modificationCount ) { forceModelModificationCount = cg_forceModel.modificationCount; CG_ForceModelChange(); } } int CG_CrosshairPlayer( void ) { if ( cg.time > ( cg.crosshairClientTime + 1000 ) ) { return -1; } return cg.crosshairClientNum; } int CG_LastAttacker( void ) { if ( !cg.attackerTime ) { return -1; } return cg.snap->ps.persistant[PERS_ATTACKER]; } void QDECL CG_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); trap_Print( text ); } void QDECL CG_Error( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); trap_Error( text ); } void QDECL Com_Error( int level, const char *error, ... ) { va_list argptr; char text[1024]; va_start (argptr, error); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); CG_Error( "%s", text); } void QDECL Com_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); CG_Printf ("%s", text); } /* ================ CG_Argv ================ */ const char *CG_Argv( int arg ) { static char buffer[MAX_STRING_CHARS]; trap_Argv( arg, buffer, sizeof( buffer ) ); return buffer; } //======================================================================== /* ================= CG_RegisterItemSounds The server says this item is used on this level ================= */ static void CG_RegisterItemSounds( int itemNum ) { gitem_t *item; char data[MAX_QPATH]; char *s, *start; int len; item = &bg_itemlist[ itemNum ]; if( item->pickup_sound ) { trap_S_RegisterSound( item->pickup_sound, qfalse ); } // parse the space seperated precache string for other media s = item->sounds; if (!s || !s[0]) return; while (*s) { start = s; while (*s && *s != ' ') { s++; } len = s-start; if (len >= MAX_QPATH || len < 5) { CG_Error( "PrecacheItem: %s has bad precache string", item->classname); return; } memcpy (data, start, len); data[len] = 0; if ( *s ) { s++; } if ( !strcmp(data+len-3, "wav" )) { trap_S_RegisterSound( data, qfalse ); } } } /* ================= CG_RegisterSounds called during a precache command ================= */ static void CG_RegisterSounds( void ) { int i; char items[MAX_ITEMS+1]; char name[MAX_QPATH]; const char *soundName; // voice commands #ifdef MISSIONPACK CG_LoadVoiceChats(); #endif cgs.media.oneMinuteSound = trap_S_RegisterSound( "sound/feedback/1_minute.wav", qtrue ); cgs.media.fiveMinuteSound = trap_S_RegisterSound( "sound/feedback/5_minute.wav", qtrue ); cgs.media.suddenDeathSound = trap_S_RegisterSound( "sound/feedback/sudden_death.wav", qtrue ); cgs.media.oneFragSound = trap_S_RegisterSound( "sound/feedback/1_frag.wav", qtrue ); cgs.media.twoFragSound = trap_S_RegisterSound( "sound/feedback/2_frags.wav", qtrue ); cgs.media.threeFragSound = trap_S_RegisterSound( "sound/feedback/3_frags.wav", qtrue ); cgs.media.count3Sound = trap_S_RegisterSound( "sound/feedback/three.wav", qtrue ); cgs.media.count2Sound = trap_S_RegisterSound( "sound/feedback/two.wav", qtrue ); cgs.media.count1Sound = trap_S_RegisterSound( "sound/feedback/one.wav", qtrue ); cgs.media.countFightSound = trap_S_RegisterSound( "sound/feedback/fight.wav", qtrue ); cgs.media.countPrepareSound = trap_S_RegisterSound( "sound/feedback/prepare.wav", qtrue ); #ifdef MISSIONPACK cgs.media.countPrepareTeamSound = trap_S_RegisterSound( "sound/feedback/prepare_team.wav", qtrue ); #endif // N_G: Another condition that makes no sense to me, see for // yourself if you really meant this // Sago: Makes perfect sense: Load team game stuff if the gametype is a teamgame and not an exception (like GT_LMS) if ( ( ( cgs.gametype >= GT_TEAM ) && ( cgs.ffa_gt != 1 ) ) || cg_buildScript.integer ) { cgs.media.captureAwardSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); cgs.media.redLeadsSound = trap_S_RegisterSound( "sound/feedback/redleads.wav", qtrue ); cgs.media.blueLeadsSound = trap_S_RegisterSound( "sound/feedback/blueleads.wav", qtrue ); cgs.media.teamsTiedSound = trap_S_RegisterSound( "sound/feedback/teamstied.wav", qtrue ); cgs.media.hitTeamSound = trap_S_RegisterSound( "sound/feedback/hit_teammate.wav", qtrue ); cgs.media.redScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_red_scores.wav", qtrue ); cgs.media.blueScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_scores.wav", qtrue ); cgs.media.captureYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); cgs.media.captureOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_opponent.wav", qtrue ); cgs.media.returnYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_yourteam.wav", qtrue ); cgs.media.returnOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); cgs.media.takenYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_yourteam.wav", qtrue ); cgs.media.takenOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_opponent.wav", qtrue ); if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION|| cg_buildScript.integer ) { cgs.media.redFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_red_returned.wav", qtrue ); cgs.media.blueFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_returned.wav", qtrue ); cgs.media.enemyTookYourFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_flag.wav", qtrue ); cgs.media.yourTeamTookEnemyFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_flag.wav", qtrue ); } if ( cgs.gametype == GT_1FCTF || cg_buildScript.integer ) { // FIXME: get a replacement for this sound ? cgs.media.neutralFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); cgs.media.yourTeamTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_1flag.wav", qtrue ); cgs.media.enemyTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_1flag.wav", qtrue ); } if ( cgs.gametype == GT_1FCTF || cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION ||cg_buildScript.integer ) { cgs.media.youHaveFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_you_flag.wav", qtrue ); cgs.media.holyShitSound = trap_S_RegisterSound("sound/feedback/voc_holyshit.wav", qtrue); } if ( cgs.gametype == GT_OBELISK || cg_buildScript.integer ) { cgs.media.yourBaseIsUnderAttackSound = trap_S_RegisterSound( "sound/teamplay/voc_base_attack.wav", qtrue ); } } cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/machinegun/buletby1.wav", qfalse ); cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse ); cgs.media.wearOffSound = trap_S_RegisterSound( "sound/items/wearoff.wav", qfalse ); cgs.media.useNothingSound = trap_S_RegisterSound( "sound/items/use_nothing.wav", qfalse ); cgs.media.gibSound = trap_S_RegisterSound( "sound/player/gibsplt1.wav", qfalse ); cgs.media.gibBounce1Sound = trap_S_RegisterSound( "sound/player/gibimp1.wav", qfalse ); cgs.media.gibBounce2Sound = trap_S_RegisterSound( "sound/player/gibimp2.wav", qfalse ); cgs.media.gibBounce3Sound = trap_S_RegisterSound( "sound/player/gibimp3.wav", qfalse ); // LEILEI cgs.media.lspl1Sound = trap_S_RegisterSound( "sound/le/splat1.wav", qfalse ); cgs.media.lspl2Sound = trap_S_RegisterSound( "sound/le/splat2.wav", qfalse ); cgs.media.lspl3Sound = trap_S_RegisterSound( "sound/le/splat3.wav", qfalse ); cgs.media.lbul1Sound = trap_S_RegisterSound( "sound/le/bullet1.wav", qfalse ); cgs.media.lbul2Sound = trap_S_RegisterSound( "sound/le/bullet2.wav", qfalse ); cgs.media.lbul3Sound = trap_S_RegisterSound( "sound/le/bullet3.wav", qfalse ); cgs.media.lshl1Sound = trap_S_RegisterSound( "sound/le/shell1.wav", qfalse ); cgs.media.lshl2Sound = trap_S_RegisterSound( "sound/le/shell2.wav", qfalse ); cgs.media.lshl3Sound = trap_S_RegisterSound( "sound/le/shell3.wav", qfalse ); cgs.media.useInvulnerabilitySound = trap_S_RegisterSound( "sound/items/invul_activate.wav", qfalse ); cgs.media.invulnerabilityImpactSound1 = trap_S_RegisterSound( "sound/items/invul_impact_01.wav", qfalse ); cgs.media.invulnerabilityImpactSound2 = trap_S_RegisterSound( "sound/items/invul_impact_02.wav", qfalse ); cgs.media.invulnerabilityImpactSound3 = trap_S_RegisterSound( "sound/items/invul_impact_03.wav", qfalse ); cgs.media.invulnerabilityJuicedSound = trap_S_RegisterSound( "sound/items/invul_juiced.wav", qfalse ); cgs.media.ammoregenSound = trap_S_RegisterSound("sound/items/cl_ammoregen.wav", qfalse); cgs.media.doublerSound = trap_S_RegisterSound("sound/items/cl_doubler.wav", qfalse); cgs.media.guardSound = trap_S_RegisterSound("sound/items/cl_guard.wav", qfalse); cgs.media.scoutSound = trap_S_RegisterSound("sound/items/cl_scout.wav", qfalse); cgs.media.obeliskHitSound1 = trap_S_RegisterSound( "sound/items/obelisk_hit_01.wav", qfalse ); cgs.media.obeliskHitSound2 = trap_S_RegisterSound( "sound/items/obelisk_hit_02.wav", qfalse ); cgs.media.obeliskHitSound3 = trap_S_RegisterSound( "sound/items/obelisk_hit_03.wav", qfalse ); cgs.media.obeliskRespawnSound = trap_S_RegisterSound( "sound/items/obelisk_respawn.wav", qfalse ); cgs.media.teleInSound = trap_S_RegisterSound( "sound/world/telein.wav", qfalse ); cgs.media.teleOutSound = trap_S_RegisterSound( "sound/world/teleout.wav", qfalse ); cgs.media.respawnSound = trap_S_RegisterSound( "sound/items/respawn1.wav", qfalse ); cgs.media.noAmmoSound = trap_S_RegisterSound( "sound/weapons/noammo.wav", qfalse ); cgs.media.talkSound = trap_S_RegisterSound( "sound/player/talk.wav", qfalse ); cgs.media.landSound = trap_S_RegisterSound( "sound/player/land1.wav", qfalse); switch(cg_hitsound.integer) { case 0: default: cgs.media.hitSound = trap_S_RegisterSound( "sound/feedback/hit.wav", qfalse ); }; #ifdef MISSIONPACK cgs.media.hitSoundHighArmor = trap_S_RegisterSound( "sound/feedback/hithi.wav", qfalse ); cgs.media.hitSoundLowArmor = trap_S_RegisterSound( "sound/feedback/hitlo.wav", qfalse ); #endif cgs.media.impressiveSound = trap_S_RegisterSound( "sound/feedback/impressive.wav", qtrue ); cgs.media.excellentSound = trap_S_RegisterSound( "sound/feedback/excellent.wav", qtrue ); cgs.media.deniedSound = trap_S_RegisterSound( "sound/feedback/denied.wav", qtrue ); cgs.media.humiliationSound = trap_S_RegisterSound( "sound/feedback/humiliation.wav", qtrue ); cgs.media.assistSound = trap_S_RegisterSound( "sound/feedback/assist.wav", qtrue ); cgs.media.defendSound = trap_S_RegisterSound( "sound/feedback/defense.wav", qtrue ); #ifdef MISSIONPACK cgs.media.firstImpressiveSound = trap_S_RegisterSound( "sound/feedback/first_impressive.wav", qtrue ); cgs.media.firstExcellentSound = trap_S_RegisterSound( "sound/feedback/first_excellent.wav", qtrue ); cgs.media.firstHumiliationSound = trap_S_RegisterSound( "sound/feedback/first_gauntlet.wav", qtrue ); #endif cgs.media.takenLeadSound = trap_S_RegisterSound( "sound/feedback/takenlead.wav", qtrue); cgs.media.tiedLeadSound = trap_S_RegisterSound( "sound/feedback/tiedlead.wav", qtrue); cgs.media.lostLeadSound = trap_S_RegisterSound( "sound/feedback/lostlead.wav", qtrue); #ifdef MISSIONPACK cgs.media.voteNow = trap_S_RegisterSound( "sound/feedback/vote_now.wav", qtrue); cgs.media.votePassed = trap_S_RegisterSound( "sound/feedback/vote_passed.wav", qtrue); cgs.media.voteFailed = trap_S_RegisterSound( "sound/feedback/vote_failed.wav", qtrue); #endif cgs.media.watrInSound = trap_S_RegisterSound( "sound/player/watr_in.wav", qfalse); cgs.media.watrOutSound = trap_S_RegisterSound( "sound/player/watr_out.wav", qfalse); cgs.media.watrUnSound = trap_S_RegisterSound( "sound/player/watr_un.wav", qfalse); cgs.media.jumpPadSound = trap_S_RegisterSound ("sound/world/jumppad.wav", qfalse ); for (i=0 ; i<4 ; i++) { Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_NORMAL][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_BOOT][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/flesh%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_FLESH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/mech%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_MECH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/energy%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_ENERGY][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/splash%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_SPLASH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/clank%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_METAL][i] = trap_S_RegisterSound (name, qfalse); } // only register the items that the server says we need Q_strncpyz(items, CG_ConfigString(CS_ITEMS), sizeof(items)); for ( i = 1 ; i < bg_numItems ; i++ ) { // if ( items[ i ] == '1' || cg_buildScript.integer ) { CG_RegisterItemSounds( i ); // } } for ( i = 1 ; i < MAX_SOUNDS ; i++ ) { soundName = CG_ConfigString( CS_SOUNDS+i ); if ( !soundName[0] ) { break; } if ( soundName[0] == '*' ) { continue; // custom sound } cgs.gameSounds[i] = trap_S_RegisterSound( soundName, qfalse ); } // FIXME: only needed with item cgs.media.flightSound = trap_S_RegisterSound( "sound/items/flight.wav", qfalse ); cgs.media.medkitSound = trap_S_RegisterSound ("sound/items/use_medkit.wav", qfalse); cgs.media.quadSound = trap_S_RegisterSound("sound/items/damage3.wav", qfalse); cgs.media.sfx_ric1 = trap_S_RegisterSound ("sound/weapons/machinegun/ric1.wav", qfalse); cgs.media.sfx_ric2 = trap_S_RegisterSound ("sound/weapons/machinegun/ric2.wav", qfalse); cgs.media.sfx_ric3 = trap_S_RegisterSound ("sound/weapons/machinegun/ric3.wav", qfalse); cgs.media.sfx_railg = trap_S_RegisterSound ("sound/weapons/railgun/railgf1a.wav", qfalse); cgs.media.sfx_rockexp = trap_S_RegisterSound ("sound/weapons/rocket/rocklx1a.wav", qfalse); cgs.media.sfx_plasmaexp = trap_S_RegisterSound ("sound/weapons/plasma/plasmx1a.wav", qfalse); cgs.media.sfx_proxexp = trap_S_RegisterSound( "sound/weapons/proxmine/wstbexpl.wav" , qfalse); cgs.media.sfx_nghit = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpd.wav" , qfalse); cgs.media.sfx_nghitflesh = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpl.wav" , qfalse); cgs.media.sfx_nghitmetal = trap_S_RegisterSound( "sound/weapons/nailgun/wnalimpm.wav", qfalse ); cgs.media.sfx_chghit = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpd.wav", qfalse ); cgs.media.sfx_chghitflesh = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpl.wav", qfalse ); cgs.media.sfx_chghitmetal = trap_S_RegisterSound( "sound/weapons/vulcan/wvulimpm.wav", qfalse ); cgs.media.weaponHoverSound = trap_S_RegisterSound( "sound/weapons/weapon_hover.wav", qfalse ); cgs.media.kamikazeExplodeSound = trap_S_RegisterSound( "sound/items/kam_explode.wav", qfalse ); cgs.media.kamikazeImplodeSound = trap_S_RegisterSound( "sound/items/kam_implode.wav", qfalse ); cgs.media.kamikazeFarSound = trap_S_RegisterSound( "sound/items/kam_explode_far.wav", qfalse ); cgs.media.winnerSound = trap_S_RegisterSound( "sound/feedback/voc_youwin.wav", qfalse ); cgs.media.loserSound = trap_S_RegisterSound( "sound/feedback/voc_youlose.wav", qfalse ); cgs.media.youSuckSound = trap_S_RegisterSound( "sound/misc/yousuck.wav", qfalse ); cgs.media.wstbimplSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpl.wav", qfalse); cgs.media.wstbimpmSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpm.wav", qfalse); cgs.media.wstbimpdSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbimpd.wav", qfalse); cgs.media.wstbactvSound = trap_S_RegisterSound("sound/weapons/proxmine/wstbactv.wav", qfalse); cgs.media.regenSound = trap_S_RegisterSound("sound/items/regen.wav", qfalse); cgs.media.protectSound = trap_S_RegisterSound("sound/items/protect3.wav", qfalse); cgs.media.n_healthSound = trap_S_RegisterSound("sound/items/n_health.wav", qfalse ); cgs.media.hgrenb1aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb1a.wav", qfalse); cgs.media.hgrenb2aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb2a.wav", qfalse); #ifdef MISSIONPACK trap_S_RegisterSound("sound/player/sergei/death1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/death2.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/death3.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/jump1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/pain25_1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/pain75_1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/pain100_1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/falling1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/gasp.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/drown.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/fall1.wav", qfalse ); trap_S_RegisterSound("sound/player/sergei/taunt.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/death1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/death2.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/death3.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/jump1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/pain25_1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/pain75_1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/pain100_1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/falling1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/gasp.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/drown.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/fall1.wav", qfalse ); trap_S_RegisterSound("sound/player/kyonshi/taunt.wav", qfalse ); #endif } //=================================================================================== /* ================= CG_RegisterGraphics This function may execute for a couple of minutes with a slow disk. ================= */ static void CG_RegisterGraphics( void ) { int i; char items[MAX_ITEMS+1]; static char *sb_nums[11] = { "gfx/2d/numbers/zero_32b", "gfx/2d/numbers/one_32b", "gfx/2d/numbers/two_32b", "gfx/2d/numbers/three_32b", "gfx/2d/numbers/four_32b", "gfx/2d/numbers/five_32b", "gfx/2d/numbers/six_32b", "gfx/2d/numbers/seven_32b", "gfx/2d/numbers/eight_32b", "gfx/2d/numbers/nine_32b", "gfx/2d/numbers/minus_32b", }; // clear any references to old media memset( &cg.refdef, 0, sizeof( cg.refdef ) ); trap_R_ClearScene(); CG_LoadingString( cgs.mapname ); trap_R_LoadWorldMap( cgs.mapname ); // precache status bar pics CG_LoadingString( "game media" ); for ( i=0 ; i<11 ; i++) { cgs.media.numberShaders[i] = trap_R_RegisterShader( sb_nums[i] ); } cgs.media.botSkillShaders[0] = trap_R_RegisterShader( "menu/art/skill1.tga" ); cgs.media.botSkillShaders[1] = trap_R_RegisterShader( "menu/art/skill2.tga" ); cgs.media.botSkillShaders[2] = trap_R_RegisterShader( "menu/art/skill3.tga" ); cgs.media.botSkillShaders[3] = trap_R_RegisterShader( "menu/art/skill4.tga" ); cgs.media.botSkillShaders[4] = trap_R_RegisterShader( "menu/art/skill5.tga" ); cgs.media.viewBloodShader = trap_R_RegisterShader( "viewBloodBlend" ); cgs.media.deferShader = trap_R_RegisterShaderNoMip( "gfx/2d/defer.tga" ); cgs.media.scoreboardName = trap_R_RegisterShaderNoMip( "menu/tab/name.tga" ); cgs.media.scoreboardPing = trap_R_RegisterShaderNoMip( "menu/tab/ping.tga" ); cgs.media.scoreboardScore = trap_R_RegisterShaderNoMip( "menu/tab/score.tga" ); cgs.media.scoreboardTime = trap_R_RegisterShaderNoMip( "menu/tab/time.tga" ); cgs.media.smokePuffShader = trap_R_RegisterShader( "smokePuff" ); cgs.media.smokePuffRageProShader = trap_R_RegisterShader( "smokePuffRagePro" ); cgs.media.shotgunSmokePuffShader = trap_R_RegisterShader( "shotgunSmokePuff" ); cgs.media.nailPuffShader = trap_R_RegisterShader( "nailtrail" ); cgs.media.blueProxMine = trap_R_RegisterModel( "models/weaphits/proxmineb.md3" ); cgs.media.plasmaBallShader = trap_R_RegisterShader( "sprites/plasma1" ); cgs.media.bloodTrailShader = trap_R_RegisterShader( "bloodTrail" ); cgs.media.lagometerShader = trap_R_RegisterShader("lagometer" ); cgs.media.connectionShader = trap_R_RegisterShader( "disconnected" ); cgs.media.waterBubbleShader = trap_R_RegisterShader( "waterBubble" ); cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" ); cgs.media.selectShader = trap_R_RegisterShader( "gfx/2d/select" ); for (i = 0; i < NUM_CROSSHAIRS; i++ ) { if (i < 10) cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) ); else cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%02d", i - 10) ); } cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" ); cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" ); // powerup shaders cgs.media.quadShader = trap_R_RegisterShader("powerups/quad" ); cgs.media.quadWeaponShader = trap_R_RegisterShader("powerups/quadWeapon" ); cgs.media.battleSuitShader = trap_R_RegisterShader("powerups/battleSuit" ); cgs.media.battleWeaponShader = trap_R_RegisterShader("powerups/battleWeapon" ); cgs.media.invisShader = trap_R_RegisterShader("powerups/invisibility" ); cgs.media.regenShader = trap_R_RegisterShader("powerups/regen" ); cgs.media.hastePuffShader = trap_R_RegisterShader("hasteSmokePuff" ); if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION|| cgs.gametype == GT_1FCTF || cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { cgs.media.redCubeModel = trap_R_RegisterModel( "models/powerups/orb/r_orb.md3" ); cgs.media.blueCubeModel = trap_R_RegisterModel( "models/powerups/orb/b_orb.md3" ); cgs.media.redCubeIcon = trap_R_RegisterShader( "icons/skull_red" ); cgs.media.blueCubeIcon = trap_R_RegisterShader( "icons/skull_blue" ); } if( ( cgs.gametype >= GT_TEAM ) && ( cgs.ffa_gt != 1 ) ) { cgs.media.redOverlay = trap_R_RegisterShader( "playeroverlays/playerSuit1_Red"); cgs.media.blueOverlay = trap_R_RegisterShader( "playeroverlays/playerSuit1_Blue"); } else { cgs.media.neutralOverlay = trap_R_RegisterShader( "playeroverlays/playerSuit1_Neutral"); } //For Double Domination: if ( cgs.gametype == GT_DOUBLE_D ) { cgs.media.ddPointSkinA[TEAM_RED] = trap_R_RegisterShaderNoMip( "icons/icona_red" ); cgs.media.ddPointSkinA[TEAM_BLUE] = trap_R_RegisterShaderNoMip( "icons/icona_blue" ); cgs.media.ddPointSkinA[TEAM_FREE] = trap_R_RegisterShaderNoMip( "icons/icona_white" ); cgs.media.ddPointSkinA[TEAM_NONE] = trap_R_RegisterShaderNoMip( "icons/noammo" ); cgs.media.ddPointSkinB[TEAM_RED] = trap_R_RegisterShaderNoMip( "icons/iconb_red" ); cgs.media.ddPointSkinB[TEAM_BLUE] = trap_R_RegisterShaderNoMip( "icons/iconb_blue" ); cgs.media.ddPointSkinB[TEAM_FREE] = trap_R_RegisterShaderNoMip( "icons/iconb_white" ); cgs.media.ddPointSkinB[TEAM_NONE] = trap_R_RegisterShaderNoMip( "icons/noammo" ); } if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_1FCTF || cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { cgs.media.redFlagModel = trap_R_RegisterModel( "models/flags/r_flag.md3" ); cgs.media.blueFlagModel = trap_R_RegisterModel( "models/flags/b_flag.md3" ); cgs.media.neutralFlagModel = trap_R_RegisterModel( "models/flags/n_flag.md3" ); cgs.media.redFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_red1" ); cgs.media.redFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_red2" ); cgs.media.redFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_red3" ); cgs.media.blueFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_blu1" ); cgs.media.blueFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_blu2" ); cgs.media.blueFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu3" ); cgs.media.flagPoleModel = trap_R_RegisterModel( "models/flag2/flagpole.md3" ); cgs.media.flagFlapModel = trap_R_RegisterModel( "models/flag2/flagflap3.md3" ); cgs.media.redFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/red.skin" ); cgs.media.blueFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/blue.skin" ); cgs.media.neutralFlagFlapSkin = trap_R_RegisterSkin( "models/flag2/white.skin" ); cgs.media.redFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/red_base.md3" ); cgs.media.blueFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/blue_base.md3" ); cgs.media.neutralFlagBaseModel = trap_R_RegisterModel( "models/mapobjects/flagbase/ntrl_base.md3" ); } if ( cgs.gametype == GT_1FCTF || cg_buildScript.integer ) { cgs.media.neutralFlagModel = trap_R_RegisterModel( "models/flags/n_flag.md3" ); cgs.media.flagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_neutral1" ); cgs.media.flagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_red2" ); cgs.media.flagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu2" ); cgs.media.flagShader[3] = trap_R_RegisterShaderNoMip( "icons/iconf_neutral3" ); } if ( cgs.gametype == GT_OBELISK || cg_buildScript.integer ) { cgs.media.rocketExplosionShader = trap_R_RegisterShader("rocketExplosion"); cgs.media.overloadBaseModel = trap_R_RegisterModel( "models/powerups/overload_base.md3" ); cgs.media.overloadTargetModel = trap_R_RegisterModel( "models/powerups/overload_target.md3" ); cgs.media.overloadLightsModel = trap_R_RegisterModel( "models/powerups/overload_lights.md3" ); cgs.media.overloadEnergyModel = trap_R_RegisterModel( "models/powerups/overload_energy.md3" ); } if ( cgs.gametype == GT_HARVESTER || cg_buildScript.integer ) { cgs.media.harvesterModel = trap_R_RegisterModel( "models/powerups/harvester/harvester.md3" ); cgs.media.harvesterRedSkin = trap_R_RegisterSkin( "models/powerups/harvester/red.skin" ); cgs.media.harvesterBlueSkin = trap_R_RegisterSkin( "models/powerups/harvester/blue.skin" ); cgs.media.harvesterNeutralModel = trap_R_RegisterModel( "models/powerups/obelisk/obelisk.md3" ); } cgs.media.redKamikazeShader = trap_R_RegisterShader( "models/weaphits/kamikred" ); cgs.media.dustPuffShader = trap_R_RegisterShader("hasteSmokePuff" ); if ( ( ( cgs.gametype >= GT_TEAM ) && ( cgs.ffa_gt != 1 ) ) || cg_buildScript.integer ) { cgs.media.friendShader = trap_R_RegisterShader( "sprites/foe" ); cgs.media.redQuadShader = trap_R_RegisterShader("powerups/blueflag" ); //cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" ); - moved outside, used by accuracy cgs.media.blueKamikazeShader = trap_R_RegisterShader( "models/weaphits/kamikblu" ); } cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" ); cgs.media.armorModel = trap_R_RegisterModel( "models/powerups/armor/armor_yel.md3" ); cgs.media.armorIcon = trap_R_RegisterShaderNoMip( "icons/iconr_yellow" ); cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" ); cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); cgs.media.gibAbdomen = trap_R_RegisterModel( "models/gibs/abdomen.md3" ); cgs.media.gibArm = trap_R_RegisterModel( "models/gibs/arm.md3" ); cgs.media.gibChest = trap_R_RegisterModel( "models/gibs/chest.md3" ); cgs.media.gibFist = trap_R_RegisterModel( "models/gibs/fist.md3" ); cgs.media.gibFoot = trap_R_RegisterModel( "models/gibs/foot.md3" ); cgs.media.gibForearm = trap_R_RegisterModel( "models/gibs/forearm.md3" ); cgs.media.gibIntestine = trap_R_RegisterModel( "models/gibs/intestine.md3" ); cgs.media.gibLeg = trap_R_RegisterModel( "models/gibs/leg.md3" ); cgs.media.gibSkull = trap_R_RegisterModel( "models/gibs/skull.md3" ); cgs.media.gibBrain = trap_R_RegisterModel( "models/gibs/brain.md3" ); cgs.media.smoke2 = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); cgs.media.balloonShader = trap_R_RegisterShader( "sprites/balloon3" ); cgs.media.bloodExplosionShader = trap_R_RegisterShader( "bloodExplosion" ); cgs.media.bulletFlashModel = trap_R_RegisterModel("models/weaphits/bullet.md3"); cgs.media.ringFlashModel = trap_R_RegisterModel("models/weaphits/ring02.md3"); cgs.media.dishFlashModel = trap_R_RegisterModel("models/weaphits/boom01.md3"); #ifdef MISSIONPACK cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/powerups/pop.md3" ); #else cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/misc/telep.md3" ); cgs.media.teleportEffectShader = trap_R_RegisterShader( "teleportEffect" ); #endif cgs.media.kamikazeEffectModel = trap_R_RegisterModel( "models/weaphits/kamboom2.md3" ); cgs.media.kamikazeShockWave = trap_R_RegisterModel( "models/weaphits/kamwave.md3" ); cgs.media.kamikazeHeadModel = trap_R_RegisterModel( "models/powerups/kamikazi.md3" ); cgs.media.kamikazeHeadTrail = trap_R_RegisterModel( "models/powerups/trailtest.md3" ); cgs.media.guardPowerupModel = trap_R_RegisterModel( "models/powerups/guard_player.md3" ); cgs.media.scoutPowerupModel = trap_R_RegisterModel( "models/powerups/scout_player.md3" ); cgs.media.doublerPowerupModel = trap_R_RegisterModel( "models/powerups/doubler_player.md3" ); cgs.media.ammoRegenPowerupModel = trap_R_RegisterModel( "models/powerups/ammo_player.md3" ); cgs.media.invulnerabilityImpactModel = trap_R_RegisterModel( "models/powerups/shield/impact.md3" ); cgs.media.invulnerabilityJuicedModel = trap_R_RegisterModel( "models/powerups/shield/juicer.md3" ); cgs.media.medkitUsageModel = trap_R_RegisterModel( "models/powerups/regen.md3" ); cgs.media.heartShader = trap_R_RegisterShaderNoMip( "ui/assets/statusbar/selectedhealth.tga" ); cgs.media.invulnerabilityPowerupModel = trap_R_RegisterModel( "models/powerups/shield/shield.md3" ); cgs.media.medalImpressive = trap_R_RegisterShaderNoMip( "medal_impressive" ); cgs.media.medalExcellent = trap_R_RegisterShaderNoMip( "medal_excellent" ); cgs.media.medalGauntlet = trap_R_RegisterShaderNoMip( "medal_gauntlet" ); cgs.media.medalDefend = trap_R_RegisterShaderNoMip( "medal_defend" ); cgs.media.medalAssist = trap_R_RegisterShaderNoMip( "medal_assist" ); cgs.media.medalCapture = trap_R_RegisterShaderNoMip( "medal_capture" ); // LEILEI SHADERS cgs.media.lsmkShader1 = trap_R_RegisterShader("leismoke1" ); cgs.media.lsmkShader2 = trap_R_RegisterShader("leismoke2" ); cgs.media.lsmkShader3 = trap_R_RegisterShader("leismoke3" ); cgs.media.lsmkShader4 = trap_R_RegisterShader("leismoke4" ); cgs.media.lsplShader = trap_R_RegisterShader("leisplash" ); cgs.media.lspkShader1 = trap_R_RegisterShader("leispark" ); cgs.media.lspkShader2 = trap_R_RegisterShader("leispark2" ); cgs.media.lbumShader1 = trap_R_RegisterShader("leiboom1" ); cgs.media.lfblShader1 = trap_R_RegisterShader("leifball" ); cgs.media.lbldShader1 = trap_R_RegisterShader("leiblood1" ); cgs.media.lbldShader2 = trap_R_RegisterShader("leiblood2" ); // this is a mark, by the way // New Bullet Marks cgs.media.lmarkmetal1 = trap_R_RegisterShader("leimetalmark1" ); cgs.media.lmarkmetal2 = trap_R_RegisterShader("leimetalmark2" ); cgs.media.lmarkmetal3 = trap_R_RegisterShader("leimetalmark3" ); cgs.media.lmarkmetal4 = trap_R_RegisterShader("leimetalmark4" ); cgs.media.lmarkbullet1 = trap_R_RegisterShader("leibulletmark1" ); cgs.media.lmarkbullet2 = trap_R_RegisterShader("leibulletmark2" ); cgs.media.lmarkbullet3 = trap_R_RegisterShader("leibulletmark3" ); cgs.media.lmarkbullet4 = trap_R_RegisterShader("leibulletmark4" ); memset( cg_items, 0, sizeof( cg_items ) ); memset( cg_weapons, 0, sizeof( cg_weapons ) ); // only register the items that the server says we need Q_strncpyz(items, CG_ConfigString(CS_ITEMS), sizeof(items)); for ( i = 1 ; i < bg_numItems ; i++ ) { if ( items[ i ] == '1' || cg_buildScript.integer ) { CG_LoadingItem( i ); CG_RegisterItemVisuals( i ); } } // wall marks cgs.media.bulletMarkShader = trap_R_RegisterShader( "gfx/damage/bullet_mrk" ); cgs.media.burnMarkShader = trap_R_RegisterShader( "gfx/damage/burn_med_mrk" ); cgs.media.holeMarkShader = trap_R_RegisterShader( "gfx/damage/hole_lg_mrk" ); cgs.media.energyMarkShader = trap_R_RegisterShader( "gfx/damage/plasma_mrk" ); cgs.media.shadowMarkShader = trap_R_RegisterShader( "markShadow" ); cgs.media.wakeMarkShader = trap_R_RegisterShader( "wake" ); cgs.media.bloodMarkShader = trap_R_RegisterShader( "bloodMark" ); // register the inline models cgs.numInlineModels = trap_CM_NumInlineModels(); for ( i = 1 ; i < cgs.numInlineModels ; i++ ) { char name[10]; vec3_t mins, maxs; int j; Com_sprintf( name, sizeof(name), "*%i", i ); cgs.inlineDrawModel[i] = trap_R_RegisterModel( name ); trap_R_ModelBounds( cgs.inlineDrawModel[i], mins, maxs ); for ( j = 0 ; j < 3 ; j++ ) { cgs.inlineModelMidpoints[i][j] = mins[j] + 0.5 * ( maxs[j] - mins[j] ); } } // register all the server specified models for (i=1 ; i= MAX_CONFIGSTRINGS ) { CG_Error( "CG_ConfigString: bad index: %i", index ); } return cgs.gameState.stringData + cgs.gameState.stringOffsets[ index ]; } //================================================================== /* ====================== CG_StartMusic ====================== */ void CG_StartMusic( void ) { char *s; char parm1[MAX_QPATH], parm2[MAX_QPATH]; // start the background music if ( *cg_music.string && Q_stricmp( cg_music.string, "none" ) ) { s = (char *)cg_music.string; } else { s = (char *)CG_ConfigString( CS_MUSIC ); Q_strncpyz( parm1, COM_Parse( &s ), sizeof( parm1 ) ); Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) ); trap_S_StartBackgroundTrack( parm1, parm2 ); } } #ifdef MISSIONPACK char *CG_GetMenuBuffer(const char *filename) { int len; fileHandle_t f; static char buf[MAX_MENUFILE]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) ); return NULL; } if ( len >= MAX_MENUFILE ) { trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i\n", filename, len, MAX_MENUFILE ) ); trap_FS_FCloseFile( f ); return NULL; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); return buf; } // // ============================== // new hud stuff ( mission pack ) // ============================== // qboolean CG_Asset_Parse(int handle) { pc_token_t token; const char *tempStr; if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "{") != 0) { return qfalse; } while ( 1 ) { if (!trap_PC_ReadToken(handle, &token)) return qfalse; if (Q_stricmp(token.string, "}") == 0) { return qtrue; } // font if (Q_stricmp(token.string, "font") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { return qfalse; } cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.textFont); continue; } // smallFont if (Q_stricmp(token.string, "smallFont") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { return qfalse; } cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.smallFont); continue; } // font if (Q_stricmp(token.string, "bigfont") == 0) { int pointSize; if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle, &pointSize)) { return qfalse; } cgDC.registerFont(tempStr, pointSize, &cgDC.Assets.bigFont); continue; } // gradientbar if (Q_stricmp(token.string, "gradientbar") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr); continue; } // enterMenuSound if (Q_stricmp(token.string, "menuEnterSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } cgDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // exitMenuSound if (Q_stricmp(token.string, "menuExitSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } cgDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // itemFocusSound if (Q_stricmp(token.string, "itemFocusSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } cgDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } // menuBuzzSound if (Q_stricmp(token.string, "menuBuzzSound") == 0) { if (!PC_String_Parse(handle, &tempStr)) { return qfalse; } cgDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse ); continue; } if (Q_stricmp(token.string, "cursor") == 0) { if (!PC_String_Parse(handle, &cgDC.Assets.cursorStr)) { return qfalse; } cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr); continue; } if (Q_stricmp(token.string, "fadeClamp") == 0) { if (!PC_Float_Parse(handle, &cgDC.Assets.fadeClamp)) { return qfalse; } continue; } if (Q_stricmp(token.string, "fadeCycle") == 0) { if (!PC_Int_Parse(handle, &cgDC.Assets.fadeCycle)) { return qfalse; } continue; } if (Q_stricmp(token.string, "fadeAmount") == 0) { if (!PC_Float_Parse(handle, &cgDC.Assets.fadeAmount)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowX") == 0) { if (!PC_Float_Parse(handle, &cgDC.Assets.shadowX)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowY") == 0) { if (!PC_Float_Parse(handle, &cgDC.Assets.shadowY)) { return qfalse; } continue; } if (Q_stricmp(token.string, "shadowColor") == 0) { if (!PC_Color_Parse(handle, &cgDC.Assets.shadowColor)) { return qfalse; } cgDC.Assets.shadowFadeClamp = cgDC.Assets.shadowColor[3]; continue; } } return qfalse; // bk001204 - why not? } void CG_ParseMenu(const char *menuFile) { pc_token_t token; int handle; handle = trap_PC_LoadSource(menuFile); if (!handle) handle = trap_PC_LoadSource("ui/testhud.menu"); if (!handle) return; while ( 1 ) { if (!trap_PC_ReadToken( handle, &token )) { break; } //if ( Q_stricmp( token, "{" ) ) { // Com_Printf( "Missing { in menu file\n" ); // break; //} //if ( menuCount == MAX_MENUS ) { // Com_Printf( "Too many menus!\n" ); // break; //} if ( token.string[0] == '}' ) { break; } if (Q_stricmp(token.string, "assetGlobalDef") == 0) { if (CG_Asset_Parse(handle)) { continue; } else { break; } } if (Q_stricmp(token.string, "menudef") == 0) { // start a new menu Menu_New(handle); } } trap_PC_FreeSource(handle); } qboolean CG_Load_Menu(char **p) { char *token; token = COM_ParseExt(p, qtrue); if (token[0] != '{') { return qfalse; } while ( 1 ) { token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "}") == 0) { return qtrue; } if ( !token || token[0] == 0 ) { return qfalse; } CG_ParseMenu(token); } return qfalse; } void CG_LoadMenus(const char *menuFile) { char *token; char *p; int len, start; fileHandle_t f; static char buf[MAX_MENUDEFFILE]; start = trap_Milliseconds(); len = trap_FS_FOpenFile( menuFile, &f, FS_READ ); if ( !f ) { trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); len = trap_FS_FOpenFile( "ui/hud.txt", &f, FS_READ ); if (!f) { trap_Error( va( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!\n") ); } } if ( len >= MAX_MENUDEFFILE ) { trap_Error( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i\n", menuFile, len, MAX_MENUDEFFILE ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); COM_Compress(buf); Menu_Reset(); p = buf; while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if( !token || token[0] == 0 || token[0] == '}') { break; } //if ( Q_stricmp( token, "{" ) ) { // Com_Printf( "Missing { in menu file\n" ); // break; //} //if ( menuCount == MAX_MENUS ) { // Com_Printf( "Too many menus!\n" ); // break; //} if ( Q_stricmp( token, "}" ) == 0 ) { break; } if (Q_stricmp(token, "loadmenu") == 0) { if (CG_Load_Menu(&p)) { continue; } else { break; } } } Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start); } static qboolean CG_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key) { return qfalse; } static int CG_FeederCount(float feederID) { int i, count; count = 0; if (feederID == FEEDER_REDTEAM_LIST) { for (i = 0; i < cg.numScores; i++) { if (cg.scores[i].team == TEAM_RED) { count++; } } } else if (feederID == FEEDER_BLUETEAM_LIST) { for (i = 0; i < cg.numScores; i++) { if (cg.scores[i].team == TEAM_BLUE) { count++; } } } else if (feederID == FEEDER_SCOREBOARD) { return cg.numScores; } return count; } void CG_SetScoreSelection(void *p) { menuDef_t *menu = (menuDef_t*)p; playerState_t *ps = &cg.snap->ps; int i, red, blue; red = blue = 0; for (i = 0; i < cg.numScores; i++) { if (cg.scores[i].team == TEAM_RED) { red++; } else if (cg.scores[i].team == TEAM_BLUE) { blue++; } if (ps->clientNum == cg.scores[i].client) { cg.selectedScore = i; } } if (menu == NULL) { // just interested in setting the selected score return; } if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { int feeder = FEEDER_REDTEAM_LIST; i = red; if (cg.scores[cg.selectedScore].team == TEAM_BLUE) { feeder = FEEDER_BLUETEAM_LIST; i = blue; } Menu_SetFeederSelection(menu, feeder, i, NULL); } else { Menu_SetFeederSelection(menu, FEEDER_SCOREBOARD, cg.selectedScore, NULL); } } // FIXME: might need to cache this info static clientInfo_t * CG_InfoFromScoreIndex(int index, int team, int *scoreIndex) { int i, count; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { count = 0; for (i = 0; i < cg.numScores; i++) { if (cg.scores[i].team == team) { if (count == index) { *scoreIndex = i; return &cgs.clientinfo[cg.scores[i].client]; } count++; } } } *scoreIndex = index; return &cgs.clientinfo[ cg.scores[index].client ]; } static const char *CG_FeederItemText(float feederID, int index, int column, qhandle_t *handle) { gitem_t *item; int scoreIndex = 0; clientInfo_t *info = NULL; int team = -1; score_t *sp = NULL; *handle = -1; if (feederID == FEEDER_REDTEAM_LIST) { team = TEAM_RED; } else if (feederID == FEEDER_BLUETEAM_LIST) { team = TEAM_BLUE; } info = CG_InfoFromScoreIndex(index, team, &scoreIndex); sp = &cg.scores[scoreIndex]; if (info && info->infoValid) { switch (column) { case 0: if ( info->powerups & ( 1 << PW_NEUTRALFLAG ) ) { item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); *handle = cg_items[ ITEM_INDEX(item) ].icon; } else if ( info->powerups & ( 1 << PW_REDFLAG ) ) { item = BG_FindItemForPowerup( PW_REDFLAG ); *handle = cg_items[ ITEM_INDEX(item) ].icon; } else if ( info->powerups & ( 1 << PW_BLUEFLAG ) ) { item = BG_FindItemForPowerup( PW_BLUEFLAG ); *handle = cg_items[ ITEM_INDEX(item) ].icon; } else { if ( info->botSkill > 0 && info->botSkill <= 5 ) { *handle = cgs.media.botSkillShaders[ info->botSkill - 1 ]; } else if ( info->handicap < 100 ) { return va("%i", info->handicap ); } } break; case 1: if (team == -1) { return ""; } else if (info->isDead) { *handle = cgs.media.deathShader; } else { *handle = CG_StatusHandle(info->teamTask); } break; case 2: if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << sp->client ) ) { return "Ready"; } if (team == -1) { if (cgs.gametype == GT_TOURNAMENT) { return va("%i/%i", info->wins, info->losses); } else if (info->infoValid && info->team == TEAM_SPECTATOR ) { return "Spectator"; } else { return ""; } } else { if (info->teamLeader) { return "Leader"; } } break; case 3: return info->name; break; case 4: return va("%i", info->score); break; case 5: return va("%4i", sp->time); break; case 6: if ( sp->ping == -1 ) { return "connecting"; } return va("%4i", sp->ping); break; } } return ""; } static qhandle_t CG_FeederItemImage(float feederID, int index) { return 0; } static void CG_FeederSelection(float feederID, int index) { if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { int i, count; int team = (feederID == FEEDER_REDTEAM_LIST) ? TEAM_RED : TEAM_BLUE; count = 0; for (i = 0; i < cg.numScores; i++) { if (cg.scores[i].team == team) { if (index == count) { cg.selectedScore = i; } count++; } } } else { cg.selectedScore = index; } } #endif #ifdef MISSIONPACK // bk001204 - only needed there static float CG_Cvar_Get(const char *cvar) { char buff[128]; memset(buff, 0, sizeof(buff)); trap_Cvar_VariableStringBuffer(cvar, buff, sizeof(buff)); return atof(buff); } #endif #ifdef MISSIONPACK void CG_Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { CG_Text_Paint(x, y, scale, color, text, 0, limit, style); } static int CG_OwnerDrawWidth(int ownerDraw, float scale) { switch (ownerDraw) { case CG_GAME_TYPE: return CG_Text_Width(CG_GameTypeString(), scale, 0); case CG_GAME_STATUS: return CG_Text_Width(CG_GetGameStatusText(), scale, 0); break; case CG_KILLER: return CG_Text_Width(CG_GetKillerText(), scale, 0); break; case CG_RED_NAME: return CG_Text_Width(cg_redTeamName.string, scale, 0); break; case CG_BLUE_NAME: return CG_Text_Width(cg_blueTeamName.string, scale, 0); break; } return 0; } static int CG_PlayCinematic(const char *name, float x, float y, float w, float h) { return trap_CIN_PlayCinematic(name, x, y, w, h, CIN_loop); } static void CG_StopCinematic(int handle) { trap_CIN_StopCinematic(handle); } static void CG_DrawCinematic(int handle, float x, float y, float w, float h) { trap_CIN_SetExtents(handle, x, y, w, h); trap_CIN_DrawCinematic(handle); } static void CG_RunCinematicFrame(int handle) { trap_CIN_RunCinematic(handle); } /* ================= CG_LoadHudMenu(); ================= */ void CG_LoadHudMenu( void ) { char buff[1024]; const char *hudSet; cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip; cgDC.setColor = &trap_R_SetColor; cgDC.drawHandlePic = &CG_DrawPic; cgDC.drawStretchPic = &trap_R_DrawStretchPic; cgDC.drawText = &CG_Text_Paint; cgDC.textWidth = &CG_Text_Width; cgDC.textHeight = &CG_Text_Height; cgDC.registerModel = &trap_R_RegisterModel; cgDC.modelBounds = &trap_R_ModelBounds; cgDC.fillRect = &CG_FillRect; cgDC.drawRect = &CG_DrawRect; cgDC.drawSides = &CG_DrawSides; cgDC.drawTopBottom = &CG_DrawTopBottom; cgDC.clearScene = &trap_R_ClearScene; cgDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; cgDC.renderScene = &trap_R_RenderScene; cgDC.registerFont = &trap_R_RegisterFont; cgDC.ownerDrawItem = &CG_OwnerDraw; cgDC.getValue = &CG_GetValue; cgDC.ownerDrawVisible = &CG_OwnerDrawVisible; cgDC.runScript = &CG_RunMenuScript; cgDC.getTeamColor = &CG_GetTeamColor; cgDC.setCVar = trap_Cvar_Set; cgDC.getCVarString = trap_Cvar_VariableStringBuffer; cgDC.getCVarValue = CG_Cvar_Get; cgDC.drawTextWithCursor = &CG_Text_PaintWithCursor; //cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode; //cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode; cgDC.startLocalSound = &trap_S_StartLocalSound; cgDC.ownerDrawHandleKey = &CG_OwnerDrawHandleKey; cgDC.feederCount = &CG_FeederCount; cgDC.feederItemImage = &CG_FeederItemImage; cgDC.feederItemText = &CG_FeederItemText; cgDC.feederSelection = &CG_FeederSelection; //cgDC.setBinding = &trap_Key_SetBinding; //cgDC.getBindingBuf = &trap_Key_GetBindingBuf; //cgDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf; //cgDC.executeText = &trap_Cmd_ExecuteText; cgDC.Error = &Com_Error; cgDC.Print = &Com_Printf; cgDC.ownerDrawWidth = &CG_OwnerDrawWidth; //cgDC.Pause = &CG_Pause; cgDC.registerSound = &trap_S_RegisterSound; cgDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; cgDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; cgDC.playCinematic = &CG_PlayCinematic; cgDC.stopCinematic = &CG_StopCinematic; cgDC.drawCinematic = &CG_DrawCinematic; cgDC.runCinematicFrame = &CG_RunCinematicFrame; Init_Display(&cgDC); Menu_Reset(); trap_Cvar_VariableStringBuffer("cg_hudFiles", buff, sizeof(buff)); hudSet = buff; if (hudSet[0] == '\0') { hudSet = "ui/hud.txt"; } CG_LoadMenus(hudSet); } void CG_AssetCache( void ) { //if (Assets.textFont == NULL) { // trap_R_RegisterFont("fonts/arial.ttf", 72, &Assets.textFont); //} //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND ); //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus)); cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); cgDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); cgDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); cgDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); cgDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); cgDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); cgDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); cgDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); cgDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); cgDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR ); cgDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN ); cgDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP ); cgDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT ); cgDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT ); cgDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB ); cgDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR ); cgDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB ); } #endif /* ================= CG_Init Called after every level change or subsystem restart Will perform callbacks to make the loading info screen update. ================= */ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) { const char *s; // clear everything memset( &cgs, 0, sizeof( cgs ) ); memset( &cg, 0, sizeof( cg ) ); memset( cg_entities, 0, sizeof(cg_entities) ); memset( cg_weapons, 0, sizeof(cg_weapons) ); memset( cg_items, 0, sizeof(cg_items) ); cg.clientNum = clientNum; cgs.processedSnapshotNum = serverMessageNum; cgs.serverCommandSequence = serverCommandSequence; // load a few needed things before we do any screen updates cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/bigchars" ); cgs.media.whiteShader = trap_R_RegisterShader( "white" ); cgs.media.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" ); cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" ); cgs.media.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" ); CG_RegisterCvars(); CG_InitConsoleCommands(); cg.weaponSelect = WP_MACHINEGUN; cgs.redflag = cgs.blueflag = -1; // For compatibily, default to unset for cgs.flagStatus = -1; // old servers // get the rendering configuration from the client system trap_GetGlconfig( &cgs.glconfig ); cgs.screenXScale = cgs.glconfig.vidWidth / 640.0; cgs.screenYScale = cgs.glconfig.vidHeight / 480.0; // get the gamestate from the client system trap_GetGameState( &cgs.gameState ); // check version s = CG_ConfigString( CS_GAME_VERSION ); if ( strcmp( s, GAME_VERSION ) ) { CG_Error( "Client/Server game mismatch: %s/%s", GAME_VERSION, s ); } s = CG_ConfigString( CS_LEVEL_START_TIME ); cgs.levelStartTime = atoi( s ); CG_ParseServerinfo(); // load the new map CG_LoadingString( "collision map" ); trap_CM_LoadMap( cgs.mapname ); #ifdef MISSIONPACK String_Init(); #endif cg.loading = qtrue; // force players to load instead of defer CG_LoadingString( "sounds" ); CG_RegisterSounds(); CG_LoadingString( "graphics" ); CG_RegisterGraphics(); CG_LoadingString( "clients" ); CG_RegisterClients(); // if low on memory, some clients will be deferred #ifdef MISSIONPACK CG_AssetCache(); CG_LoadHudMenu(); // load new hud stuff #endif cg.loading = qfalse; // future players will be deferred CG_InitLocalEntities(); CG_InitMarkPolys(); // remove the last loading update cg.infoScreenText[0] = 0; // Make sure we have update values (scores) CG_SetConfigValues(); CG_StartMusic(); CG_LoadingString( "" ); #ifdef MISSIONPACK CG_InitTeamChat(); #endif CG_ShaderStateChanged(); //Init challenge system challenges_init(); addChallenge(GENERAL_TEST); trap_S_ClearLoopingSounds( qtrue ); } /* ================= CG_Shutdown Called before every level change or subsystem restart ================= */ void CG_Shutdown( void ) { // some mods may need to do cleanup work here, // like closing files or archiving session data challenges_save(); } /* ================== CG_EventHandling ================== type 0 - no event handling 1 - team menu 2 - hud editor */ #ifndef MISSIONPACK void CG_EventHandling(int type) { } void CG_KeyEvent(int key, qboolean down) { } void CG_MouseEvent(int x, int y) { } #endif //unlagged - attack prediction #3 // moved from g_weapon.c /* ====================== SnapVectorTowards Round a vector to integers for more efficient network transmission, but make sure that it rounds towards a given point rather than blindly truncating. This prevents it from truncating into a wall. ====================== */ void SnapVectorTowards( vec3_t v, vec3_t to ) { int i; for ( i = 0 ; i < 3 ; i++ ) { if ( to[i] <= v[i] ) { v[i] = (int)v[i]; } else { v[i] = (int)v[i] + 1; } } } //unlagged - attack prediction #3 static qboolean do_vid_restart = qfalse; void CG_FairCvars() { qboolean vid_restart_required = qfalse; char rendererinfos[128]; if(cgs.gametype == GT_SINGLE_PLAYER) { trap_Cvar_VariableStringBuffer("r_vertexlight",rendererinfos,sizeof(rendererinfos) ); if(cg_autovertex.integer && atoi( rendererinfos ) == 0 ) { trap_Cvar_Set("r_vertexlight","1"); vid_restart_required = qtrue; } return; //Don't do anything in single player } if(cgs.videoflags & VF_LOCK_CVARS_BASIC) { //Lock basic cvars. trap_Cvar_VariableStringBuffer("r_subdivisions",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 80 ) { trap_Cvar_Set("r_subdivisions","80"); vid_restart_required = qtrue; } trap_Cvar_VariableStringBuffer("cg_shadows",rendererinfos,sizeof(rendererinfos) ); if (atoi( rendererinfos )!=0 && atoi( rendererinfos )!=1 ) { trap_Cvar_Set("cg_shadows","1"); } } if(cgs.videoflags & VF_LOCK_CVARS_EXTENDED) { //Lock extended cvars. trap_Cvar_VariableStringBuffer("r_subdivisions",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 20 ) { trap_Cvar_Set("r_subdivisions","20"); vid_restart_required = qtrue; } trap_Cvar_VariableStringBuffer("r_picmip",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 3 ) { trap_Cvar_Set("r_picmip","3"); vid_restart_required = qtrue; } else if(atoi( rendererinfos ) < 0 ) { trap_Cvar_Set("r_picmip","0"); vid_restart_required = qtrue; } trap_Cvar_VariableStringBuffer("r_intensity",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 2 ) { trap_Cvar_Set("r_intensity","2"); vid_restart_required = qtrue; } else if(atoi( rendererinfos ) < 0 ) { trap_Cvar_Set("r_intensity","0"); vid_restart_required = qtrue; } trap_Cvar_VariableStringBuffer("r_mapoverbrightbits",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 2 ) { trap_Cvar_Set("r_mapoverbrightbits","2"); vid_restart_required = qtrue; } else if(atoi( rendererinfos ) < 0 ) { trap_Cvar_Set("r_mapoverbrightbits","0"); vid_restart_required = qtrue; } trap_Cvar_VariableStringBuffer("r_overbrightbits",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) > 2 ) { trap_Cvar_Set("r_overbrightbits","2"); vid_restart_required = qtrue; } else if(atoi( rendererinfos ) < 0 ) { trap_Cvar_Set("r_overbrightbits","0"); vid_restart_required = qtrue; } } if(cgs.videoflags & VF_LOCK_VERTEX) { trap_Cvar_VariableStringBuffer("r_vertexlight",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) != 0 ) { trap_Cvar_Set("r_vertexlight","0"); vid_restart_required = qtrue; } } else if(cg_autovertex.integer){ trap_Cvar_VariableStringBuffer("r_vertexlight",rendererinfos,sizeof(rendererinfos) ); if(atoi( rendererinfos ) == 0 ) { trap_Cvar_Set("r_vertexlight","1"); vid_restart_required = qtrue; } } if(vid_restart_required && do_vid_restart) trap_SendConsoleCommand("vid_restart"); do_vid_restart = qtrue; } openarena_0.8.8.orig/code/cgame/cg_view.c0000644000175000017500000005561011656310265017013 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_view.c -- setup all the parameters (position, angle, etc) // for a 3D rendering #include "cg_local.h" /* ============================================================================= MODEL TESTING The viewthing and gun positioning tools from Q2 have been integrated and enhanced into a single model testing facility. Model viewing can begin with either "testmodel " or "testgun ". The names must be the full pathname after the basedir, like "models/weapons/v_launch/tris.md3" or "players/male/tris.md3" Testmodel will create a fake entity 100 units in front of the current view position, directly facing the viewer. It will remain immobile, so you can move around it to view it from different angles. Testgun will cause the model to follow the player around and supress the real view weapon model. The default frame 0 of most guns is completely off screen, so you will probably have to cycle a couple frames to see it. "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in q3default.cfg. If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let you adjust the positioning. Note that none of the model testing features update while the game is paused, so it may be convenient to test with deathmatch set to 1 so that bringing down the console doesn't pause the game. ============================================================================= */ /* ================= CG_TestModel_f Creates an entity in front of the current position, which can then be moved around ================= */ void CG_TestModel_f (void) { vec3_t angles; memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) ); if ( trap_Argc() < 2 ) { return; } Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH ); cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); if ( trap_Argc() == 3 ) { cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) ); cg.testModelEntity.frame = 1; cg.testModelEntity.oldframe = 0; } if (! cg.testModelEntity.hModel ) { CG_Printf( "Can't register model\n" ); return; } VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin ); angles[PITCH] = 0; angles[YAW] = 180 + cg.refdefViewAngles[1]; angles[ROLL] = 0; AnglesToAxis( angles, cg.testModelEntity.axis ); cg.testGun = qfalse; } /* ================= CG_TestGun_f Replaces the current view weapon with the given model ================= */ void CG_TestGun_f (void) { CG_TestModel_f(); cg.testGun = qtrue; cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON; } void CG_TestModelNextFrame_f (void) { cg.testModelEntity.frame++; CG_Printf( "frame %i\n", cg.testModelEntity.frame ); } void CG_TestModelPrevFrame_f (void) { cg.testModelEntity.frame--; if ( cg.testModelEntity.frame < 0 ) { cg.testModelEntity.frame = 0; } CG_Printf( "frame %i\n", cg.testModelEntity.frame ); } void CG_TestModelNextSkin_f (void) { cg.testModelEntity.skinNum++; CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); } void CG_TestModelPrevSkin_f (void) { cg.testModelEntity.skinNum--; if ( cg.testModelEntity.skinNum < 0 ) { cg.testModelEntity.skinNum = 0; } CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); } static void CG_AddTestModel (void) { int i; // re-register the model, because the level may have changed cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); if (! cg.testModelEntity.hModel ) { CG_Printf ("Can't register model\n"); return; } // if testing a gun, set the origin reletive to the view origin if ( cg.testGun ) { VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin ); VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] ); VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] ); VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] ); // allow the position to be adjusted for (i=0 ; i<3 ; i++) { cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value; cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value; cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value; } } trap_R_AddRefEntityToScene( &cg.testModelEntity ); } //============================================================================ /* ================= CG_CalcVrect Sets the coordinates of the rendered window ================= */ static void CG_CalcVrect (void) { int size; // the intermission should allways be full screen if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { size = 100; } else { // bound normal viewsize if (cg_viewsize.integer < 30) { trap_Cvar_Set ("cg_viewsize","30"); size = 30; } else if (cg_viewsize.integer > 100) { trap_Cvar_Set ("cg_viewsize","100"); size = 100; } else { size = cg_viewsize.integer; } } cg.refdef.width = cgs.glconfig.vidWidth*size/100; cg.refdef.width &= ~1; cg.refdef.height = cgs.glconfig.vidHeight*size/100; cg.refdef.height &= ~1; cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2; cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2; } //============================================================================== /* =============== CG_OffsetThirdPersonView =============== */ #define FOCUS_DISTANCE 512 static void CG_OffsetThirdPersonView( void ) { vec3_t forward, right, up; vec3_t view; vec3_t focusAngles; trace_t trace; static vec3_t mins = { -4, -4, -4 }; static vec3_t maxs = { 4, 4, 4 }; vec3_t focusPoint; float focusDist; float forwardScale, sideScale; cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; VectorCopy( cg.refdefViewAngles, focusAngles ); // if dead, look at killer if ( (cg.predictedPlayerState.stats[STAT_HEALTH] <= 0) && (cgs.gametype !=GT_ELIMINATION && cgs.gametype !=GT_CTF_ELIMINATION && cgs.gametype !=GT_LMS) ) { focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; } if ( focusAngles[PITCH] > 45 ) { focusAngles[PITCH] = 45; // don't go too far overhead } AngleVectors( focusAngles, forward, NULL, NULL ); VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); VectorCopy( cg.refdef.vieworg, view ); view[2] += 8; cg.refdefViewAngles[PITCH] *= 0.5; AngleVectors( cg.refdefViewAngles, forward, right, up ); forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); // trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything if (!cg_cameraMode.integer) { CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if ( trace.fraction != 1.0 ) { VectorCopy( trace.endpos, view ); view[2] += (1.0 - trace.fraction) * 32; // try another trace to this position, because a tunnel may have the ceiling // close enogh that this is poking out CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } } VectorCopy( view, cg.refdef.vieworg ); // select pitch to look at focus point from vieword VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); if ( focusDist < 1 ) { focusDist = 1; // should never happen } cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; } // this causes a compiler bug on mac MrC compiler static void CG_StepOffset( void ) { int timeDelta; // smooth out stair climbing timeDelta = cg.time - cg.stepTime; if ( timeDelta < STEP_TIME ) { cg.refdef.vieworg[2] -= cg.stepChange * (STEP_TIME - timeDelta) / STEP_TIME; } } /* =============== CG_OffsetFirstPersonView =============== */ static void CG_OffsetFirstPersonView( void ) { float *origin; float *angles; float bob; float ratio; float delta; float speed; float f; vec3_t predictedVelocity; int timeDelta; if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { return; } origin = cg.refdef.vieworg; angles = cg.refdefViewAngles; // if dead, fix the angle and don't add any kick if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { angles[ROLL] = 40; angles[PITCH] = -15; angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW]; origin[2] += cg.predictedPlayerState.viewheight; return; } // add angles based on weapon kick VectorAdd (angles, cg.kick_angles, angles); // add angles based on damage kick if ( cg.damageTime && cgs.gametype!=GT_ELIMINATION && cgs.gametype!=GT_CTF_ELIMINATION && cgs.gametype!=GT_LMS) { ratio = cg.time - cg.damageTime; if ( ratio < DAMAGE_DEFLECT_TIME ) { ratio /= DAMAGE_DEFLECT_TIME; angles[PITCH] += ratio * cg.v_dmg_pitch; angles[ROLL] += ratio * cg.v_dmg_roll; } else { ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; if ( ratio > 0 ) { angles[PITCH] += ratio * cg.v_dmg_pitch; angles[ROLL] += ratio * cg.v_dmg_roll; } } } // add pitch based on fall kick #if 0 ratio = ( cg.time - cg.landTime) / FALL_TIME; if (ratio < 0) ratio = 0; angles[PITCH] += ratio * cg.fall_value; #endif // add angles based on velocity VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]); angles[PITCH] += delta * cg_runpitch.value; delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]); angles[ROLL] -= delta * cg_runroll.value; // add angles based on bob // make sure the bob is visible even at low speeds speed = cg.xyspeed > 200 ? cg.xyspeed : 200; delta = cg.bobfracsin * cg_bobpitch.value * speed; if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) delta *= 3; // crouching angles[PITCH] += delta; delta = cg.bobfracsin * cg_bobroll.value * speed; if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) delta *= 3; // crouching accentuates roll if (cg.bobcycle & 1) delta = -delta; angles[ROLL] += delta; //=================================== // add view height origin[2] += cg.predictedPlayerState.viewheight; // smooth out duck height changes timeDelta = cg.time - cg.duckTime; if ( timeDelta < DUCK_TIME) { cg.refdef.vieworg[2] -= cg.duckChange * (DUCK_TIME - timeDelta) / DUCK_TIME; } // add bob height bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value; if (bob > 6) { bob = 6; } origin[2] += bob; // add fall height delta = cg.time - cg.landTime; if ( delta < LAND_DEFLECT_TIME ) { f = delta / LAND_DEFLECT_TIME; cg.refdef.vieworg[2] += cg.landChange * f; } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { delta -= LAND_DEFLECT_TIME; f = 1.0 - ( delta / LAND_RETURN_TIME ); cg.refdef.vieworg[2] += cg.landChange * f; } // add step offset CG_StepOffset(); // add kick offset VectorAdd (origin, cg.kick_origin, origin); // pivot the eye based on a neck length #if 0 { #define NECK_LENGTH 8 vec3_t forward, up; cg.refdef.vieworg[2] -= NECK_LENGTH; AngleVectors( cg.refdefViewAngles, forward, NULL, up ); VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg ); } #endif } //====================================================================== void CG_ZoomDown_f( void ) { if ( cg.zoomed ) { return; } cg.zoomed = qtrue; cg.zoomTime = cg.time; } void CG_ZoomUp_f( void ) { if ( !cg.zoomed ) { return; } cg.zoomed = qfalse; cg.zoomTime = cg.time; } /* ==================== CG_CalcFov Fixed fov at intermissions, otherwise account for fov variable and zooms. ==================== */ #define WAVE_AMPLITUDE 1 #define WAVE_FREQUENCY 0.4 static int CG_CalcFov( void ) { float x; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value fov_x = 90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 90; } else { fov_x = cg_fov.value; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } if( (cgs.videoflags & VF_LOCK_CVARS_BASIC) && fov_x>140 ) fov_x = 140; } if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients zoomFov = 22.5; } else { // account for zooms zoomFov = cg_zoomFov.value; if ( zoomFov < 1 ) { zoomFov = 1; } else if ( zoomFov > 160 ) { zoomFov = 160; } if( (cgs.videoflags & VF_LOCK_CVARS_BASIC) && zoomFov>140 ) zoomFov = 140; } if ( cg.zoomed ) { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = zoomFov; } else { fov_x = fov_x + f * ( zoomFov - fov_x ); } } else { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = fov_x; } else { fov_x = zoomFov + f * ( fov_x - zoomFov ); } } } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else { inwater = qfalse; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.zoomed ) { cg.zoomSensitivity = 1; } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0; } return inwater; } /* =============== CG_DamageBlendBlob =============== */ static void CG_DamageBlendBlob( void ) { int t; int maxTime; refEntity_t ent; if ( !cg.damageValue ) { return; } //if (cg.cameraMode) { // return; //} // ragePro systems can't fade blends, so don't obscure the screen if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { return; } maxTime = DAMAGE_TIME; t = cg.time - cg.damageTime; if ( t <= 0 || t >= maxTime ) { return; } memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; ent.renderfx = RF_FIRST_PERSON; VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin ); VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin ); VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin ); ent.radius = cg.damageValue * 3; ent.customShader = cgs.media.viewBloodShader; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) ); trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_CalcViewValues Sets cg.refdef view values =============== */ static int CG_CalcViewValues( void ) { playerState_t *ps; memset( &cg.refdef, 0, sizeof( cg.refdef ) ); // strings for in game rendering // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); // calculate size of 3D view CG_CalcVrect(); ps = &cg.predictedPlayerState; /* if (cg.cameraMode) { vec3_t origin, angles; if (trap_getCameraInfo(cg.time, &origin, &angles)) { VectorCopy(origin, cg.refdef.vieworg); angles[ROLL] = 0; VectorCopy(angles, cg.refdefViewAngles); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } else { cg.cameraMode = qfalse; } } */ // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); if (cg_cameraOrbit.integer) { if (cg.time > cg.nextOrbitTime) { cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer; cg_thirdPersonAngle.value += cg_cameraOrbit.value; } } // add error decay if ( cg_errorDecay.value > 0 ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f > 0 && f < 1 ) { VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg ); } else { cg.predictedErrorTime = 0; } } if ( cg.renderingThirdPerson ) { // back away from character CG_OffsetThirdPersonView(); } else { // offset for local bobbing and kicks CG_OffsetFirstPersonView(); } // position eye reletive to origin AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); if ( cg.hyperspace ) { cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; } // field of view return CG_CalcFov(); } /* ===================== CG_PowerupTimerSounds ===================== */ static void CG_PowerupTimerSounds( void ) { int i; int t; // powerup timers going away for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { t = cg.snap->ps.powerups[i]; if ( t <= cg.time ) { continue; } if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { continue; } if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) { trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound ); } } } /* ===================== CG_AddBufferedSound ===================== */ void CG_AddBufferedSound( sfxHandle_t sfx ) { if ( !sfx ) return; cg.soundBuffer[cg.soundBufferIn] = sfx; cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER; if (cg.soundBufferIn == cg.soundBufferOut) { cg.soundBufferOut++; } } /* ===================== CG_PlayBufferedSounds ===================== */ static void CG_PlayBufferedSounds( void ) { if ( cg.soundTime < cg.time ) { if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) { trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER); cg.soundBuffer[cg.soundBufferOut] = 0; cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER; cg.soundTime = cg.time + 750; } } } //========================================================================= /* ================= CG_DrawActiveFrame Generates and draws a game scene and status information at the given time. ================= */ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) { int inwater; cg.time = serverTime; cg.demoPlayback = demoPlayback; // update cvars CG_UpdateCvars(); // if we are only updating the screen as a loading // pacifier, don't even try to read snapshots if ( cg.infoScreenText[0] != 0 ) { CG_DrawInformation(); return; } // any looped sounds will be respecified as entities // are added to the render list trap_S_ClearLoopingSounds(qfalse); // clear all the render lists trap_R_ClearScene(); // set up cg.snap and possibly cg.nextSnap CG_ProcessSnapshots(); // if we haven't received any snapshots yet, all // we can draw is the information screen if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { CG_DrawInformation(); return; } // let the client system know what our weapon and zoom settings are trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity ); // this counter will be bumped for every valid scene we generate cg.clientFrame++; // update cg.predictedPlayerState CG_PredictPlayerState(); // decide on third person view cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); // build cg.refdef inwater = CG_CalcViewValues(); // first person blend blobs, done after AnglesToAxis if ( !cg.renderingThirdPerson ) { CG_DamageBlendBlob(); } // build the render lists if ( !cg.hyperspace ) { CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct CG_AddMarks(); CG_AddParticles (); CG_AddLocalEntities(); } CG_AddViewWeapon( &cg.predictedPlayerState ); // add buffered sounds CG_PlayBufferedSounds(); // play buffered voice chats CG_PlayBufferedVoiceChats(); // finish up the rest of the refdef if ( cg.testModelEntity.hModel ) { CG_AddTestModel(); } cg.refdef.time = cg.time; memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); // warning sounds when powerup is wearing off CG_PowerupTimerSounds(); // update audio positions trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater ); // make sure the lagometerSample and frame timing isn't done twice when in stereo if ( stereoView != STEREO_RIGHT ) { cg.frametime = cg.time - cg.oldTime; if ( cg.frametime < 0 ) { cg.frametime = 0; } cg.oldTime = cg.time; CG_AddLagometerFrameInfo(); } if (cg_timescale.value != cg_timescaleFadeEnd.value) { if (cg_timescale.value < cg_timescaleFadeEnd.value) { cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; if (cg_timescale.value > cg_timescaleFadeEnd.value) cg_timescale.value = cg_timescaleFadeEnd.value; } else { cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; if (cg_timescale.value < cg_timescaleFadeEnd.value) cg_timescale.value = cg_timescaleFadeEnd.value; } if (cg_timescaleFadeSpeed.value) { trap_Cvar_Set("timescale", va("%f", cg_timescale.value)); } } // actually issue the rendering calls CG_DrawActive( stereoView ); if ( cg_stats.integer ) { CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame ); } } openarena_0.8.8.orig/code/cgame/cg_newdraw.c0000644000175000017500000014446411656310265017516 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef MISSIONPACK // bk001204 #error This file not be used for classic Q3A. #endif #include "cg_local.h" #include "../ui/ui_shared.h" extern displayContextDef_t cgDC; // set in CG_ParseTeamInfo //static int sortedTeamPlayers[TEAM_MAXOVERLAY]; //static int numSortedTeamPlayers; int drawTeamOverlayModificationCount = -1; //static char systemChat[256]; //static char teamChat1[256]; //static char teamChat2[256]; void CG_InitTeamChat(void) { memset(teamChat1, 0, sizeof(teamChat1)); memset(teamChat2, 0, sizeof(teamChat2)); memset(systemChat, 0, sizeof(systemChat)); } void CG_SetPrintString(int type, const char *p) { if (type == SYSTEM_PRINT) { Q_strncpyz(systemChat, p, sizeof(systemChat)); } else { Q_strncpyz(teamChat2, teamChat1, sizeof(teamChat2)); Q_strncpyz(teamChat1, p, sizeof(teamChat1)); } } void CG_CheckOrderPending(void) { if (cgs.gametype < GT_CTF || cgs.ffa_gt>0) { return; } if (cgs.orderPending) { //clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer]; const char *p1, *p2, *b; p1 = p2 = b = NULL; switch (cgs.currentOrder) { case TEAMTASK_OFFENSE: p1 = VOICECHAT_ONOFFENSE; p2 = VOICECHAT_OFFENSE; b = "+button7; wait; -button7"; break; case TEAMTASK_DEFENSE: p1 = VOICECHAT_ONDEFENSE; p2 = VOICECHAT_DEFEND; b = "+button8; wait; -button8"; break; case TEAMTASK_PATROL: p1 = VOICECHAT_ONPATROL; p2 = VOICECHAT_PATROL; b = "+button9; wait; -button9"; break; case TEAMTASK_FOLLOW: p1 = VOICECHAT_ONFOLLOW; p2 = VOICECHAT_FOLLOWME; b = "+button10; wait; -button10"; break; case TEAMTASK_CAMP: p1 = VOICECHAT_ONCAMPING; p2 = VOICECHAT_CAMP; break; case TEAMTASK_RETRIEVE: p1 = VOICECHAT_ONGETFLAG; p2 = VOICECHAT_RETURNFLAG; break; case TEAMTASK_ESCORT: p1 = VOICECHAT_ONFOLLOWCARRIER; p2 = VOICECHAT_FOLLOWFLAGCARRIER; break; } if (cg_currentSelectedPlayer.integer == numSortedTeamPlayers) { // to everyone trap_SendConsoleCommand(va("cmd vsay_team %s\n", p2)); } else { // for the player self if (sortedTeamPlayers[cg_currentSelectedPlayer.integer] == cg.snap->ps.clientNum && p1) { trap_SendConsoleCommand(va("teamtask %i\n", cgs.currentOrder)); //trap_SendConsoleCommand(va("cmd say_team %s\n", p2)); trap_SendConsoleCommand(va("cmd vsay_team %s\n", p1)); } else if (p2) { //trap_SendConsoleCommand(va("cmd say_team %s, %s\n", ci->name,p)); trap_SendConsoleCommand(va("cmd vtell %d %s\n", sortedTeamPlayers[cg_currentSelectedPlayer.integer], p2)); } } if (b) { trap_SendConsoleCommand(b); } cgs.orderPending = qfalse; } } static void CG_SetSelectedPlayerName( void ) { if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer]; if (ci) { trap_Cvar_Set("cg_selectedPlayerName", ci->name); trap_Cvar_Set("cg_selectedPlayer", va("%d", sortedTeamPlayers[cg_currentSelectedPlayer.integer])); cgs.currentOrder = ci->teamTask; } } else { trap_Cvar_Set("cg_selectedPlayerName", "Everyone"); } } int CG_GetSelectedPlayer( void ) { if (cg_currentSelectedPlayer.integer < 0 || cg_currentSelectedPlayer.integer >= numSortedTeamPlayers) { cg_currentSelectedPlayer.integer = 0; } return cg_currentSelectedPlayer.integer; } void CG_SelectNextPlayer( void ) { CG_CheckOrderPending(); if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { cg_currentSelectedPlayer.integer++; } else { cg_currentSelectedPlayer.integer = 0; } CG_SetSelectedPlayerName(); } void CG_SelectPrevPlayer( void ) { CG_CheckOrderPending(); if (cg_currentSelectedPlayer.integer > 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) { cg_currentSelectedPlayer.integer--; } else { cg_currentSelectedPlayer.integer = numSortedTeamPlayers; } CG_SetSelectedPlayerName(); } static void CG_DrawPlayerArmorIcon( rectDef_t *rect, qboolean draw2D ) { centity_t *cent; playerState_t *ps; vec3_t angles; vec3_t origin; if ( cg_drawStatus.integer == 0 ) { return; } cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; if ( draw2D || ( !cg_draw3dIcons.integer && cg_drawIcons.integer) ) { // bk001206 - parentheses CG_DrawPic( rect->x, rect->y + rect->h/2 + 1, rect->w, rect->h, cgs.media.armorIcon ); } else if (cg_draw3dIcons.integer) { VectorClear( angles ); origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cgs.media.armorModel, 0, origin, angles ); } } static void CG_DrawPlayerArmorValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { char num[16]; int value; centity_t *cent; playerState_t *ps; cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; value = ps->stats[STAT_ARMOR]; if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", value); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } #ifndef MISSIONPACK // bk001206 static float healthColors[4][4] = { // { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; // bk0101016 - float const { 1.0f, 0.69f, 0.0f, 1.0f } , // normal { 1.0f, 0.2f, 0.2f, 1.0f }, // low health { 0.5f, 0.5f, 0.5f, 1.0f}, // weapon firing { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 #endif static void CG_DrawPlayerAmmoIcon( rectDef_t *rect, qboolean draw2D ) { centity_t *cent; playerState_t *ps; vec3_t angles; vec3_t origin; cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; if ( draw2D || (!cg_draw3dIcons.integer && cg_drawIcons.integer) ) { // bk001206 - parentheses qhandle_t icon; icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; if ( icon ) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, icon ); } } else if (cg_draw3dIcons.integer) { if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { VectorClear( angles ); origin[0] = 70; origin[1] = 0; origin[2] = 0; angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); } } } static void CG_DrawPlayerAmmoValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { char num[16]; int value; centity_t *cent; playerState_t *ps; cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; if ( cent->currentState.weapon ) { value = ps->ammo[cent->currentState.weapon]; if ( value > -1 ) { if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", value); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } } } static void CG_DrawPlayerHead(rectDef_t *rect, qboolean draw2D) { vec3_t angles; float size, stretch; float frac; float x = rect->x; VectorClear( angles ); if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) { frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME; size = rect->w * 1.25 * ( 1.5 - frac * 0.5 ); stretch = size - rect->w * 1.25; // kick in the direction of damage x -= stretch * 0.5 + cg.damageX * stretch * 0.5; cg.headStartYaw = 180 + cg.damageX * 45; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); cg.headStartTime = cg.time; cg.headEndTime = cg.time + 100 + random() * 2000; } else { if ( cg.time >= cg.headEndTime ) { // select a new head angle cg.headStartYaw = cg.headEndYaw; cg.headStartPitch = cg.headEndPitch; cg.headStartTime = cg.headEndTime; cg.headEndTime = cg.time + 100 + random() * 2000; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); } size = rect->w * 1.25; } // if the server was frozen for a while we may have a bad head start time if ( cg.headStartTime > cg.time ) { cg.headStartTime = cg.time; } frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime ); frac = frac * frac * ( 3 - 2 * frac ); angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac; angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac; CG_DrawHead( x, rect->y, rect->w, rect->h, cg.snap->ps.clientNum, angles ); } static void CG_DrawSelectedPlayerHealth( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { clientInfo_t *ci; int value; char num[16]; ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", ci->health); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } } static void CG_DrawSelectedPlayerArmor( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { clientInfo_t *ci; int value; char num[16]; ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { if (ci->armor > 0) { if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", ci->armor); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } } } qhandle_t CG_StatusHandle(int task) { qhandle_t h = cgs.media.assaultShader; switch (task) { case TEAMTASK_OFFENSE : h = cgs.media.assaultShader; break; case TEAMTASK_DEFENSE : h = cgs.media.defendShader; break; case TEAMTASK_PATROL : h = cgs.media.patrolShader; break; case TEAMTASK_FOLLOW : h = cgs.media.followShader; break; case TEAMTASK_CAMP : h = cgs.media.campShader; break; case TEAMTASK_RETRIEVE : h = cgs.media.retrieveShader; break; case TEAMTASK_ESCORT : h = cgs.media.escortShader; break; default : h = cgs.media.assaultShader; break; } return h; } static void CG_DrawSelectedPlayerStatus( rectDef_t *rect ) { clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { qhandle_t h; if(ci->isDead) return; if (cgs.orderPending) { // blink the icon if ( cg.time > cgs.orderTime - 2500 && (cg.time >> 9 ) & 1 ) { return; } h = CG_StatusHandle(cgs.currentOrder); } else { h = CG_StatusHandle(ci->teamTask); } CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h ); } } static void CG_DrawPlayerStatus( rectDef_t *rect ) { clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum]; if (ci) { qhandle_t h = CG_StatusHandle(ci->teamTask); if(ci->isDead) h = cgs.media.deathShader; CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h); } } static void CG_DrawSelectedPlayerName( rectDef_t *rect, float scale, vec4_t color, qboolean voice, int textStyle) { clientInfo_t *ci; ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]); if (ci) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, ci->name, 0, 0, textStyle); } } static void CG_DrawSelectedPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle ) { clientInfo_t *ci; ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { const char *p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) { p = "unknown"; } CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle); } } static void CG_DrawPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle ) { clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum]; if (ci) { const char *p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) { p = "unknown"; } CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle); } } static void CG_DrawSelectedPlayerWeapon( rectDef_t *rect ) { clientInfo_t *ci; ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { if ( cg_weapons[ci->curWeapon].weaponIcon ) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ci->curWeapon].weaponIcon ); } else { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader); } } } static void CG_DrawPlayerScore( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { char num[16]; int value = cg.snap->ps.persistant[PERS_SCORE]; if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", value); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } static void CG_DrawPlayerItem( rectDef_t *rect, float scale, qboolean draw2D) { int value; vec3_t origin, angles; value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM]; if ( value ) { CG_RegisterItemVisuals( value ); if (qtrue) { CG_RegisterItemVisuals( value ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon ); } else { VectorClear( angles ); origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; CG_Draw3DModel(rect->x, rect->y, rect->w, rect->h, cg_items[ value ].models[0], 0, origin, angles ); } } } static void CG_DrawSelectedPlayerPowerup( rectDef_t *rect, qboolean draw2D ) { clientInfo_t *ci; int j; float x, y; ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; if (ci) { x = rect->x; y = rect->y; for (j = 0; j < PW_NUM_POWERUPS; j++) { if (ci->powerups & (1 << j)) { gitem_t *item; item = BG_FindItemForPowerup( j ); if (item) { CG_DrawPic( x, y, rect->w, rect->h, trap_R_RegisterShader( item->icon ) ); x += 3; y += 3; return; } } } } } static void CG_DrawSelectedPlayerHead( rectDef_t *rect, qboolean draw2D, qboolean voice ) { clipHandle_t cm; clientInfo_t *ci; float len; vec3_t origin; vec3_t mins, maxs, angles; ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]); if (ci) { if ( cg_draw3dIcons.integer ) { cm = ci->headModel; if ( !cm ) { return; } // offset the origin y and z to center the head trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the head nearly fills the box // assume heads are taller than wide len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) // allow per-model tweaking VectorAdd( origin, ci->headOffset, origin ); angles[PITCH] = 0; angles[YAW] = 180; angles[ROLL] = 0; CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, ci->headModel, ci->headSkin, origin, angles ); } else if ( cg_drawIcons.integer ) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, ci->modelIcon ); } // if they are deferred, draw a cross out if ( ci->deferred ) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader ); } } } static void CG_DrawPlayerHealth(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { playerState_t *ps; int value; char num[16]; ps = &cg.snap->ps; value = ps->stats[STAT_HEALTH]; if (shader) { trap_R_SetColor( color ); CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader); trap_R_SetColor( NULL ); } else { Com_sprintf (num, sizeof(num), "%i", value); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } } static void CG_DrawRedScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { int value; char num[16]; if ( cgs.scores1 == SCORE_NOT_PRESENT ) { Com_sprintf (num, sizeof(num), "-"); } else { Com_sprintf (num, sizeof(num), "%i", cgs.scores1); } value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } static void CG_DrawBlueScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { int value; char num[16]; if ( cgs.scores2 == SCORE_NOT_PRESENT ) { Com_sprintf (num, sizeof(num), "-"); } else { Com_sprintf (num, sizeof(num), "%i", cgs.scores2); } value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle); } // FIXME: team name support static void CG_DrawRedName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_redTeamName.string , 0, 0, textStyle); } static void CG_DrawBlueName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_blueTeamName.string, 0, 0, textStyle); } static void CG_DrawBlueFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { int i; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle); return; } } } static void CG_DrawBlueFlagStatus(rectDef_t *rect, qhandle_t shader) { if (cgs.gametype != GT_CTF && cgs.gametype != GT_CTF_ELIMINATION && cgs.gametype != GT_1FCTF) { if (cgs.gametype == GT_HARVESTER) { vec4_t color = {0, 0, 1, 1}; trap_R_SetColor(color); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.blueCubeIcon ); trap_R_SetColor(NULL); } return; } if (shader) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); } else { gitem_t *item = BG_FindItemForPowerup( PW_BLUEFLAG ); if (item) { vec4_t color = {0, 0, 1, 1}; trap_R_SetColor(color); if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.blueflag] ); } else { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] ); } trap_R_SetColor(NULL); } } } static void CG_DrawBlueFlagHead(rectDef_t *rect) { int i; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) { vec3_t angles; VectorClear( angles ); angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );; CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles ); return; } } } static void CG_DrawRedFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) { int i; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle); return; } } } static void CG_DrawRedFlagStatus(rectDef_t *rect, qhandle_t shader) { if (cgs.gametype != GT_CTF && cgs.gametype != GT_CTF_ELIMINATION && cgs.gametype != GT_1FCTF) { if (cgs.gametype == GT_HARVESTER) { vec4_t color = {1, 0, 0, 1}; trap_R_SetColor(color); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.redCubeIcon ); trap_R_SetColor(NULL); } return; } if (shader) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); } else { gitem_t *item = BG_FindItemForPowerup( PW_REDFLAG ); if (item) { vec4_t color = {1, 0, 0, 1}; trap_R_SetColor(color); if( cgs.redflag >= 0 && cgs.redflag <= 2) { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.redflag] ); } else { CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] ); } trap_R_SetColor(NULL); } } } static void CG_DrawRedFlagHead(rectDef_t *rect) { int i; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) { vec3_t angles; VectorClear( angles ); angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );; CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles ); return; } } } static void CG_HarvesterSkulls(rectDef_t *rect, float scale, vec4_t color, qboolean force2D, int textStyle ) { char num[16]; vec3_t origin, angles; qhandle_t handle; int value = cg.snap->ps.generic1; if (cgs.gametype != GT_HARVESTER) { return; } if( value > 99 ) { value = 99; } Com_sprintf (num, sizeof(num), "%i", value); value = CG_Text_Width(num, scale, 0); CG_Text_Paint(rect->x + (rect->w - value), rect->y + rect->h, scale, color, num, 0, 0, textStyle); if (cg_drawIcons.integer) { if (!force2D && cg_draw3dIcons.integer) { VectorClear(angles); origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { handle = cgs.media.redCubeModel; } else { handle = cgs.media.blueCubeModel; } CG_Draw3DModel( rect->x, rect->y, 35, 35, handle, 0, origin, angles ); } else { if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { handle = cgs.media.redCubeIcon; } else { handle = cgs.media.blueCubeIcon; } CG_DrawPic( rect->x + 3, rect->y + 16, 20, 20, handle ); } } } static void CG_OneFlagStatus(rectDef_t *rect) { if (cgs.gametype != GT_1FCTF) { return; } else { gitem_t *item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); if (item) { if( cgs.flagStatus >= 0 && cgs.flagStatus <= 4 ) { vec4_t color = {1, 1, 1, 1}; int index = 0; if (cgs.flagStatus == FLAG_TAKEN_RED) { color[1] = color[2] = 0; index = 1; } else if (cgs.flagStatus == FLAG_TAKEN_BLUE) { color[0] = color[1] = 0; index = 1; } else if (cgs.flagStatus == FLAG_DROPPED) { index = 2; } trap_R_SetColor(color); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[index] ); } } } } static void CG_DrawCTFPowerUp(rectDef_t *rect) { int value; if (cgs.gametype < GT_CTF || cgs.ffa_gt>0) { return; } value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP]; if ( value ) { CG_RegisterItemVisuals( value ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon ); } } static void CG_DrawTeamColor(rectDef_t *rect, vec4_t color) { CG_DrawTeamBackground(rect->x, rect->y, rect->w, rect->h, color[3], cg.snap->ps.persistant[PERS_TEAM]); } static void CG_DrawAreaPowerUp(rectDef_t *rect, int align, float special, float scale, vec4_t color) { char num[16]; int sorted[MAX_POWERUPS]; int sortedTime[MAX_POWERUPS]; int i, j, k; int active; playerState_t *ps; int t; gitem_t *item; float f; rectDef_t r2; float *inc; r2.x = rect->x; r2.y = rect->y; r2.w = rect->w; r2.h = rect->h; inc = (align == HUD_VERTICAL) ? &r2.y : &r2.x; ps = &cg.snap->ps; if ( ps->stats[STAT_HEALTH] <= 0 ) { return; } // sort the list by time remaining active = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( !ps->powerups[ i ] ) { continue; } t = ps->powerups[ i ] - cg.time; // ZOID--don't draw if the power up has unlimited time (999 seconds) // This is true of the CTF flags if ( t <= 0 || t >= 999000) { continue; } // insert into the list for ( j = 0 ; j < active ; j++ ) { if ( sortedTime[j] >= t ) { for ( k = active - 1 ; k >= j ; k-- ) { sorted[k+1] = sorted[k]; sortedTime[k+1] = sortedTime[k]; } break; } } sorted[j] = i; sortedTime[j] = t; active++; } // draw the icons and timers for ( i = 0 ; i < active ; i++ ) { item = BG_FindItemForPowerup( sorted[i] ); if (item) { t = ps->powerups[ sorted[i] ]; if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { trap_R_SetColor( NULL ); } else { vec4_t modulate; f = (float)( t - cg.time ) / POWERUP_BLINK_TIME; f -= (int)f; modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; trap_R_SetColor( modulate ); } CG_DrawPic( r2.x, r2.y, r2.w * .75, r2.h, trap_R_RegisterShader( item->icon ) ); Com_sprintf (num, sizeof(num), "%i", sortedTime[i] / 1000); CG_Text_Paint(r2.x + (r2.w * .75) + 3 , r2.y + r2.h, scale, color, num, 0, 0, 0); *inc += r2.w + special; } } trap_R_SetColor( NULL ); } float CG_GetValue(int ownerDraw) { centity_t *cent; clientInfo_t *ci; playerState_t *ps; cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; switch (ownerDraw) { case CG_SELECTEDPLAYER_ARMOR: ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; return ci->armor; break; case CG_SELECTEDPLAYER_HEALTH: ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()]; return ci->health; break; case CG_PLAYER_ARMOR_VALUE: return ps->stats[STAT_ARMOR]; break; case CG_PLAYER_AMMO_VALUE: if ( cent->currentState.weapon ) { return ps->ammo[cent->currentState.weapon]; } break; case CG_PLAYER_SCORE: return cg.snap->ps.persistant[PERS_SCORE]; break; case CG_PLAYER_HEALTH: return ps->stats[STAT_HEALTH]; break; case CG_RED_SCORE: return cgs.scores1; break; case CG_BLUE_SCORE: return cgs.scores2; break; default: break; } return -1; } qboolean CG_OtherTeamHasFlag(void) { if (cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_1FCTF) { int team = cg.snap->ps.persistant[PERS_TEAM]; if (cgs.gametype == GT_1FCTF) { if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_BLUE) { return qtrue; } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_RED) { return qtrue; } else { return qfalse; } } else { if (team == TEAM_RED && cgs.redflag == FLAG_TAKEN) { return qtrue; } else if (team == TEAM_BLUE && cgs.blueflag == FLAG_TAKEN) { return qtrue; } else { return qfalse; } } } return qfalse; } qboolean CG_YourTeamHasFlag(void) { if (cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_1FCTF) { int team = cg.snap->ps.persistant[PERS_TEAM]; if (cgs.gametype == GT_1FCTF) { if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_RED) { return qtrue; } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_BLUE) { return qtrue; } else { return qfalse; } } else { if (team == TEAM_RED && cgs.blueflag == FLAG_TAKEN) { return qtrue; } else if (team == TEAM_BLUE && cgs.redflag == FLAG_TAKEN) { return qtrue; } else { return qfalse; } } } return qfalse; } // THINKABOUTME: should these be exclusive or inclusive.. // qboolean CG_OwnerDrawVisible(int flags) { if (flags & CG_SHOW_TEAMINFO) { return (cg_currentSelectedPlayer.integer == numSortedTeamPlayers); } if (flags & CG_SHOW_NOTEAMINFO) { return !(cg_currentSelectedPlayer.integer == numSortedTeamPlayers); } if (flags & CG_SHOW_OTHERTEAMHASFLAG) { return CG_OtherTeamHasFlag(); } if (flags & CG_SHOW_YOURTEAMHASENEMYFLAG) { return CG_YourTeamHasFlag(); } if (flags & (CG_SHOW_BLUE_TEAM_HAS_REDFLAG | CG_SHOW_RED_TEAM_HAS_BLUEFLAG)) { if (flags & CG_SHOW_BLUE_TEAM_HAS_REDFLAG && (cgs.redflag == FLAG_TAKEN || cgs.flagStatus == FLAG_TAKEN_RED)) { return qtrue; } else if (flags & CG_SHOW_RED_TEAM_HAS_BLUEFLAG && (cgs.blueflag == FLAG_TAKEN || cgs.flagStatus == FLAG_TAKEN_BLUE)) { return qtrue; } return qfalse; } if (flags & CG_SHOW_ANYTEAMGAME) { if( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { return qtrue; } } if (flags & CG_SHOW_ANYNONTEAMGAME) { if( cgs.gametype < GT_TEAM || cgs.ffa_gt==1) { return qtrue; } } if (flags & CG_SHOW_HARVESTER) { if( cgs.gametype == GT_HARVESTER ) { return qtrue; } else { return qfalse; } } if (flags & CG_SHOW_ONEFLAG) { if( cgs.gametype == GT_1FCTF ) { return qtrue; } else { return qfalse; } } if (flags & CG_SHOW_CTF) { if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION) { return qtrue; } } if (flags & CG_SHOW_OBELISK) { if( cgs.gametype == GT_OBELISK ) { return qtrue; } else { return qfalse; } } if (flags & CG_SHOW_HEALTHCRITICAL) { if (cg.snap->ps.stats[STAT_HEALTH] < 25) { return qtrue; } } if (flags & CG_SHOW_HEALTHOK) { if (cg.snap->ps.stats[STAT_HEALTH] >= 25) { return qtrue; } } if (flags & CG_SHOW_SINGLEPLAYER) { if( cgs.gametype == GT_SINGLE_PLAYER ) { return qtrue; } } if (flags & CG_SHOW_TOURNAMENT) { if( cgs.gametype == GT_TOURNAMENT ) { return qtrue; } } if (flags & CG_SHOW_DURINGINCOMINGVOICE) { } if (flags & CG_SHOW_IF_PLAYER_HAS_FLAG) { if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { return qtrue; } } return qfalse; } static void CG_DrawPlayerHasFlag(rectDef_t *rect, qboolean force2D) { int adj = (force2D) ? 0 : 2; if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) { CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_RED, force2D); } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) { CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_BLUE, force2D); } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) { CG_DrawFlagModel( rect->x + adj, rect->y + adj, rect->w - adj, rect->h - adj, TEAM_FREE, force2D); } } static void CG_DrawAreaSystemChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, systemChat, 0, 0, 0); } static void CG_DrawAreaTeamChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color,teamChat1, 0, 0, 0); } static void CG_DrawAreaChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, teamChat2, 0, 0, 0); } const char *CG_GetKillerText(void) { const char *s = ""; if ( cg.killerName[0] ) { s = va("Fragged by %s", cg.killerName ); } return s; } static void CG_DrawKiller(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { // fragged by ... line if ( cg.killerName[0] ) { int x = rect->x + rect->w / 2; CG_Text_Paint(x - CG_Text_Width(CG_GetKillerText(), scale, 0) / 2, rect->y + rect->h, scale, color, CG_GetKillerText(), 0, 0, textStyle); } } static void CG_DrawCapFragLimit(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { int limit = (cgs.gametype >= GT_CTF && cgs.ffa_gt==0) ? cgs.capturelimit : cgs.fraglimit; CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", limit),0, 0, textStyle); } static void CG_Draw1stPlace(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { if (cgs.scores1 != SCORE_NOT_PRESENT) { CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", cgs.scores1),0, 0, textStyle); } } static void CG_Draw2ndPlace(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) { if (cgs.scores2 != SCORE_NOT_PRESENT) { CG_Text_Paint(rect->x, rect->y, scale, color, va("%2i", cgs.scores2),0, 0, textStyle); } } const char *CG_GetGameStatusText(void) { const char *s = ""; if ( cgs.gametype < GT_TEAM || cgs.ffa_gt==1) { if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { s = va("%s place with %i",CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),cg.snap->ps.persistant[PERS_SCORE] ); } } else { if ( cg.teamScores[0] == cg.teamScores[1] ) { s = va("Teams are tied at %i", cg.teamScores[0] ); } else if ( cg.teamScores[0] >= cg.teamScores[1] ) { s = va("Red leads Blue, %i to %i", cg.teamScores[0], cg.teamScores[1] ); } else { s = va("Blue leads Red, %i to %i", cg.teamScores[1], cg.teamScores[0] ); } } return s; } static void CG_DrawGameStatus(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, CG_GetGameStatusText(), 0, 0, textStyle); } const char *CG_GameTypeString(void) { if ( cgs.gametype == GT_FFA ) { return "Free For All"; } else if ( cgs.gametype == GT_TEAM ) { return "Team Deathmatch"; } else if ( cgs.gametype == GT_CTF ) { return "Capture the Flag"; } else if ( cgs.gametype == GT_1FCTF ) { return "One Flag CTF"; } else if ( cgs.gametype == GT_OBELISK ) { return "Overload"; } else if ( cgs.gametype == GT_HARVESTER ) { return "Harvester"; } else if ( cgs.gametype == GT_ELIMINATION ) { return "Elimination"; } else if ( cgs.gametype == GT_CTF_ELIMINATION ) { return "CTF Elimination"; } else if ( cgs.gametype == GT_LMS ) { return "Last Man Standing"; } else if ( cgs.gametype == GT_DOUBLE_D ) { return "Double Domination"; } return ""; } static void CG_DrawGameType(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, CG_GameTypeString(), 0, 0, textStyle); } static void CG_Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) { int len, count; vec4_t newColor; glyphInfo_t *glyph; if (text) { // TTimo: FIXME // const unsigned char *s = text; // bk001206 - unsigned const char *s = text; float max = *maxX; float useScale; fontInfo_t *font = &cgDC.Assets.textFont; if (scale <= cg_smallFont.value) { font = &cgDC.Assets.smallFont; } else if (scale > cg_bigFont.value) { font = &cgDC.Assets.bigFont; } useScale = scale * font->glyphScale; trap_R_SetColor( color ); len = strlen(text); if (limit > 0 && len > limit) { len = limit; } count = 0; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if (CG_Text_Width(s, useScale, 1) + x > max) { *maxX = 0; break; } CG_Text_PaintChar(x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph); x += (glyph->xSkip * useScale) + adjust; *maxX = x; count++; s++; } } trap_R_SetColor( NULL ); } } #define PIC_WIDTH 12 void CG_DrawNewTeamInfo(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color, qhandle_t shader) { int xx; float y; int i, j, len, count; const char *p; vec4_t hcolor; float pwidth, lwidth, maxx, leftOver; clientInfo_t *ci; gitem_t *item; qhandle_t h; // max player name width pwidth = 0; count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { len = CG_Text_Width( ci->name, scale, 0); if (len > pwidth) pwidth = len; } } // max location name width lwidth = 0; for (i = 1; i < MAX_LOCATIONS; i++) { p = CG_ConfigString(CS_LOCATIONS + i); if (p && *p) { len = CG_Text_Width(p, scale, 0); if (len > lwidth) lwidth = len; } } y = rect->y; for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { xx = rect->x + 1; for (j = 0; j <= PW_NUM_POWERUPS; j++) { if (ci->powerups & (1 << j)) { item = BG_FindItemForPowerup( j ); if (item) { CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, trap_R_RegisterShader( item->icon ) ); xx += PIC_WIDTH; } } } // FIXME: max of 3 powerups shown properly xx = rect->x + (PIC_WIDTH * 3) + 2; CG_GetColorForHealth( ci->health, ci->armor, hcolor ); trap_R_SetColor(hcolor); CG_DrawPic( xx, y + 1, PIC_WIDTH - 2, PIC_WIDTH - 2, cgs.media.heartShader ); //Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor); //CG_Text_Paint(xx, y + text_y, scale, hcolor, st, 0, 0); // draw weapon icon xx += PIC_WIDTH + 1; // weapon used is not that useful, use the space for task #if 0 if ( cg_weapons[ci->curWeapon].weaponIcon ) { CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, cg_weapons[ci->curWeapon].weaponIcon ); } else { CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, cgs.media.deferShader ); } #endif trap_R_SetColor(NULL); if(ci->isDead) { h = cgs.media.deathShader; } else if (cgs.orderPending) { // blink the icon if ( cg.time > cgs.orderTime - 2500 && (cg.time >> 9 ) & 1 ) { h = 0; } else { h = CG_StatusHandle(cgs.currentOrder); } } else { h = CG_StatusHandle(ci->teamTask); } if (h) { CG_DrawPic( xx, y, PIC_WIDTH, PIC_WIDTH, h); } xx += PIC_WIDTH + 1; leftOver = rect->w - xx; maxx = xx + leftOver / 3; CG_Text_Paint_Limit(&maxx, xx, y + text_y, scale, color, ci->name, 0, 0); p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) { p = "unknown"; } xx += leftOver / 3 + 2; maxx = rect->w - 4; CG_Text_Paint_Limit(&maxx, xx, y + text_y, scale, color, p, 0, 0); y += text_y + 2; if ( y + text_y + 2 > rect->y + rect->h ) { break; } } } } void CG_DrawTeamSpectators(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { if (cg.spectatorLen) { float maxX; if (cg.spectatorWidth == -1) { cg.spectatorWidth = 0; cg.spectatorPaintX = rect->x + 1; cg.spectatorPaintX2 = -1; } if (cg.spectatorOffset > cg.spectatorLen) { cg.spectatorOffset = 0; cg.spectatorPaintX = rect->x + 1; cg.spectatorPaintX2 = -1; } if (cg.time > cg.spectatorTime) { cg.spectatorTime = cg.time + 10; if (cg.spectatorPaintX <= rect->x + 2) { if (cg.spectatorOffset < cg.spectatorLen) { cg.spectatorPaintX += CG_Text_Width(&cg.spectatorList[cg.spectatorOffset], scale, 1) - 1; cg.spectatorOffset++; } else { cg.spectatorOffset = 0; if (cg.spectatorPaintX2 >= 0) { cg.spectatorPaintX = cg.spectatorPaintX2; } else { cg.spectatorPaintX = rect->x + rect->w - 2; } cg.spectatorPaintX2 = -1; } } else { cg.spectatorPaintX--; if (cg.spectatorPaintX2 >= 0) { cg.spectatorPaintX2--; } } } maxX = rect->x + rect->w - 2; CG_Text_Paint_Limit(&maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color, &cg.spectatorList[cg.spectatorOffset], 0, 0); if (cg.spectatorPaintX2 >= 0) { float maxX2 = rect->x + rect->w - 2; CG_Text_Paint_Limit(&maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale, color, cg.spectatorList, 0, cg.spectatorOffset); } if (cg.spectatorOffset && maxX > 0) { // if we have an offset ( we are skipping the first part of the string ) and we fit the string if (cg.spectatorPaintX2 == -1) { cg.spectatorPaintX2 = rect->x + rect->w - 2; } } else { cg.spectatorPaintX2 = -1; } } } void CG_DrawMedal(int ownerDraw, rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { score_t *score = &cg.scores[cg.selectedScore]; float value = 0; char *text = NULL; color[3] = 0.25; switch (ownerDraw) { case CG_ACCURACY: value = score->accuracy; break; case CG_ASSISTS: value = score->assistCount; break; case CG_DEFEND: value = score->defendCount; break; case CG_EXCELLENT: value = score->excellentCount; break; case CG_IMPRESSIVE: value = score->impressiveCount; break; case CG_PERFECT: value = score->perfect; break; case CG_GAUNTLET: value = score->guantletCount; break; case CG_CAPTURES: value = score->captures; break; } if (value > 0) { if (ownerDraw != CG_PERFECT) { if (ownerDraw == CG_ACCURACY) { text = va("%i%%", (int)value); if (value > 50) { color[3] = 1.0; } } else { text = va("%i", (int)value); color[3] = 1.0; } } else { if (value) { color[3] = 1.0; } text = "Wow"; } } trap_R_SetColor(color); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); if (text) { color[3] = 1.0; value = CG_Text_Width(text, scale, 0); CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h + 10 , scale, color, text, 0, 0, 0); } trap_R_SetColor(NULL); } // void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle) { rectDef_t rect; if ( cg_drawStatus.integer == 0 ) { return; } //if (ownerDrawFlags != 0 && !CG_OwnerDrawVisible(ownerDrawFlags)) { // return; //} rect.x = x; rect.y = y; rect.w = w; rect.h = h; switch (ownerDraw) { case CG_PLAYER_ARMOR_ICON: CG_DrawPlayerArmorIcon(&rect, ownerDrawFlags & CG_SHOW_2DONLY); break; case CG_PLAYER_ARMOR_ICON2D: CG_DrawPlayerArmorIcon(&rect, qtrue); break; case CG_PLAYER_ARMOR_VALUE: CG_DrawPlayerArmorValue(&rect, scale, color, shader, textStyle); break; case CG_PLAYER_AMMO_ICON: CG_DrawPlayerAmmoIcon(&rect, ownerDrawFlags & CG_SHOW_2DONLY); break; case CG_PLAYER_AMMO_ICON2D: CG_DrawPlayerAmmoIcon(&rect, qtrue); break; case CG_PLAYER_AMMO_VALUE: CG_DrawPlayerAmmoValue(&rect, scale, color, shader, textStyle); break; case CG_SELECTEDPLAYER_HEAD: CG_DrawSelectedPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY, qfalse); break; case CG_VOICE_HEAD: CG_DrawSelectedPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY, qtrue); break; case CG_VOICE_NAME: CG_DrawSelectedPlayerName(&rect, scale, color, qtrue, textStyle); break; case CG_SELECTEDPLAYER_STATUS: CG_DrawSelectedPlayerStatus(&rect); break; case CG_SELECTEDPLAYER_ARMOR: CG_DrawSelectedPlayerArmor(&rect, scale, color, shader, textStyle); break; case CG_SELECTEDPLAYER_HEALTH: CG_DrawSelectedPlayerHealth(&rect, scale, color, shader, textStyle); break; case CG_SELECTEDPLAYER_NAME: CG_DrawSelectedPlayerName(&rect, scale, color, qfalse, textStyle); break; case CG_SELECTEDPLAYER_LOCATION: CG_DrawSelectedPlayerLocation(&rect, scale, color, textStyle); break; case CG_SELECTEDPLAYER_WEAPON: CG_DrawSelectedPlayerWeapon(&rect); break; case CG_SELECTEDPLAYER_POWERUP: CG_DrawSelectedPlayerPowerup(&rect, ownerDrawFlags & CG_SHOW_2DONLY); break; case CG_PLAYER_HEAD: CG_DrawPlayerHead(&rect, ownerDrawFlags & CG_SHOW_2DONLY); break; case CG_PLAYER_ITEM: CG_DrawPlayerItem(&rect, scale, ownerDrawFlags & CG_SHOW_2DONLY); break; case CG_PLAYER_SCORE: CG_DrawPlayerScore(&rect, scale, color, shader, textStyle); break; case CG_PLAYER_HEALTH: CG_DrawPlayerHealth(&rect, scale, color, shader, textStyle); break; case CG_RED_SCORE: CG_DrawRedScore(&rect, scale, color, shader, textStyle); break; case CG_BLUE_SCORE: CG_DrawBlueScore(&rect, scale, color, shader, textStyle); break; case CG_RED_NAME: CG_DrawRedName(&rect, scale, color, textStyle); break; case CG_BLUE_NAME: CG_DrawBlueName(&rect, scale, color, textStyle); break; case CG_BLUE_FLAGHEAD: CG_DrawBlueFlagHead(&rect); break; case CG_BLUE_FLAGSTATUS: CG_DrawBlueFlagStatus(&rect, shader); break; case CG_BLUE_FLAGNAME: CG_DrawBlueFlagName(&rect, scale, color, textStyle); break; case CG_RED_FLAGHEAD: CG_DrawRedFlagHead(&rect); break; case CG_RED_FLAGSTATUS: CG_DrawRedFlagStatus(&rect, shader); break; case CG_RED_FLAGNAME: CG_DrawRedFlagName(&rect, scale, color, textStyle); break; case CG_HARVESTER_SKULLS: CG_HarvesterSkulls(&rect, scale, color, qfalse, textStyle); break; case CG_HARVESTER_SKULLS2D: CG_HarvesterSkulls(&rect, scale, color, qtrue, textStyle); break; case CG_ONEFLAG_STATUS: CG_OneFlagStatus(&rect); break; case CG_PLAYER_LOCATION: CG_DrawPlayerLocation(&rect, scale, color, textStyle); break; case CG_TEAM_COLOR: CG_DrawTeamColor(&rect, color); break; case CG_CTF_POWERUP: CG_DrawCTFPowerUp(&rect); break; case CG_AREA_POWERUP: CG_DrawAreaPowerUp(&rect, align, special, scale, color); break; case CG_PLAYER_STATUS: CG_DrawPlayerStatus(&rect); break; case CG_PLAYER_HASFLAG: CG_DrawPlayerHasFlag(&rect, qfalse); break; case CG_PLAYER_HASFLAG2D: CG_DrawPlayerHasFlag(&rect, qtrue); break; case CG_AREA_SYSTEMCHAT: CG_DrawAreaSystemChat(&rect, scale, color, shader); break; case CG_AREA_TEAMCHAT: CG_DrawAreaTeamChat(&rect, scale, color, shader); break; case CG_AREA_CHAT: CG_DrawAreaChat(&rect, scale, color, shader); break; case CG_GAME_TYPE: CG_DrawGameType(&rect, scale, color, shader, textStyle); break; case CG_GAME_STATUS: CG_DrawGameStatus(&rect, scale, color, shader, textStyle); break; case CG_KILLER: CG_DrawKiller(&rect, scale, color, shader, textStyle); break; case CG_ACCURACY: case CG_ASSISTS: case CG_DEFEND: case CG_EXCELLENT: case CG_IMPRESSIVE: case CG_PERFECT: case CG_GAUNTLET: case CG_CAPTURES: CG_DrawMedal(ownerDraw, &rect, scale, color, shader); break; case CG_SPECTATORS: CG_DrawTeamSpectators(&rect, scale, color, shader); break; case CG_TEAMINFO: if (cg_currentSelectedPlayer.integer == numSortedTeamPlayers) { CG_DrawNewTeamInfo(&rect, text_x, text_y, scale, color, shader); } break; case CG_CAPFRAGLIMIT: CG_DrawCapFragLimit(&rect, scale, color, shader, textStyle); break; case CG_1STPLACE: CG_Draw1stPlace(&rect, scale, color, shader, textStyle); break; case CG_2NDPLACE: CG_Draw2ndPlace(&rect, scale, color, shader, textStyle); break; default: break; } } void CG_MouseEvent(int x, int y) { int n; if ( (cg.predictedPlayerState.pm_type == PM_NORMAL || cg.predictedPlayerState.pm_type == PM_SPECTATOR) && cg.showScores == qfalse) { trap_Key_SetCatcher(0); return; } cgs.cursorX+= x; if (cgs.cursorX < 0) cgs.cursorX = 0; else if (cgs.cursorX > 640) cgs.cursorX = 640; cgs.cursorY += y; if (cgs.cursorY < 0) cgs.cursorY = 0; else if (cgs.cursorY > 480) cgs.cursorY = 480; n = Display_CursorType(cgs.cursorX, cgs.cursorY); cgs.activeCursor = 0; if (n == CURSOR_ARROW) { cgs.activeCursor = cgs.media.selectCursor; } else if (n == CURSOR_SIZER) { cgs.activeCursor = cgs.media.sizeCursor; } if (cgs.capturedItem) { Display_MouseMove(cgs.capturedItem, x, y); } else { Display_MouseMove(NULL, cgs.cursorX, cgs.cursorY); } } /* ================== CG_HideTeamMenus ================== */ void CG_HideTeamMenu( void ) { Menus_CloseByName("teamMenu"); Menus_CloseByName("getMenu"); } /* ================== CG_ShowTeamMenus ================== */ void CG_ShowTeamMenu( void ) { Menus_OpenByName("teamMenu"); } /* ================== CG_EventHandling ================== type 0 - no event handling 1 - team menu 2 - hud editor */ void CG_EventHandling(int type) { cgs.eventHandling = type; if (type == CGAME_EVENT_NONE) { CG_HideTeamMenu(); } else if (type == CGAME_EVENT_TEAMMENU) { //CG_ShowTeamMenu(); } else if (type == CGAME_EVENT_SCOREBOARD) { } } void CG_KeyEvent(int key, qboolean down) { if (!down) { return; } if ( cg.predictedPlayerState.pm_type == PM_NORMAL || (cg.predictedPlayerState.pm_type == PM_SPECTATOR && cg.showScores == qfalse)) { CG_EventHandling(CGAME_EVENT_NONE); trap_Key_SetCatcher(0); return; } //if (key == trap_Key_GetKey("teamMenu") || !Display_CaptureItem(cgs.cursorX, cgs.cursorY)) { // if we see this then we should always be visible // CG_EventHandling(CGAME_EVENT_NONE); // trap_Key_SetCatcher(0); //} Display_HandleKey(key, down, cgs.cursorX, cgs.cursorY); if (cgs.capturedItem) { cgs.capturedItem = NULL; } else { if (key == K_MOUSE2 && down) { cgs.capturedItem = Display_CaptureItem(cgs.cursorX, cgs.cursorY); } } } int CG_ClientNumFromName(const char *p) { int i; for (i = 0; i < cgs.maxclients; i++) { if (cgs.clientinfo[i].infoValid && Q_stricmp(cgs.clientinfo[i].name, p) == 0) { return i; } } return -1; } void CG_ShowResponseHead(void) { Menus_OpenByName("voiceMenu"); trap_Cvar_Set("cl_conXOffset", "72"); cg.voiceTime = cg.time; } void CG_RunMenuScript(char **args) { } void CG_GetTeamColor(vec4_t *color) { if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED) { (*color)[0] = 1.0f; (*color)[3] = 0.25f; (*color)[1] = (*color)[2] = 0.0f; } else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE) { (*color)[0] = (*color)[1] = 0.0f; (*color)[2] = 1.0f; (*color)[3] = 0.25f; } else { (*color)[0] = (*color)[2] = 0.0f; (*color)[1] = 0.17f; (*color)[3] = 0.25f; } } openarena_0.8.8.orig/code/cgame/cg_info.c0000644000175000017500000002006011656310265016763 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_info.c -- display information while data is being loading #include "cg_local.h" #define MAX_LOADING_PLAYER_ICONS 16 #define MAX_LOADING_ITEM_ICONS 26 static int loadingPlayerIconCount; static int loadingItemIconCount; static qhandle_t loadingPlayerIcons[MAX_LOADING_PLAYER_ICONS]; static qhandle_t loadingItemIcons[MAX_LOADING_ITEM_ICONS]; /* =================== CG_DrawLoadingIcons =================== */ static void CG_DrawLoadingIcons( void ) { int n; int x, y; for( n = 0; n < loadingPlayerIconCount; n++ ) { x = 16 + n * 78; y = 324-40; CG_DrawPic( x, y, 64, 64, loadingPlayerIcons[n] ); } for( n = 0; n < loadingItemIconCount; n++ ) { y = 400-40; if( n >= 13 ) { y += 40; } x = 16 + n % 13 * 48; CG_DrawPic( x, y, 32, 32, loadingItemIcons[n] ); } } /* ====================== CG_LoadingString ====================== */ void CG_LoadingString( const char *s ) { Q_strncpyz( cg.infoScreenText, s, sizeof( cg.infoScreenText ) ); trap_UpdateScreen(); } /* =================== CG_LoadingItem =================== */ void CG_LoadingItem( int itemNum ) { gitem_t *item; item = &bg_itemlist[itemNum]; if ( item->icon && loadingItemIconCount < MAX_LOADING_ITEM_ICONS ) { loadingItemIcons[loadingItemIconCount++] = trap_R_RegisterShaderNoMip( item->icon ); } CG_LoadingString( item->pickup_name ); } /* =================== CG_LoadingClient =================== */ void CG_LoadingClient( int clientNum ) { const char *info; char *skin; char personality[MAX_QPATH]; char model[MAX_QPATH]; char iconName[MAX_QPATH]; info = CG_ConfigString( CS_PLAYERS + clientNum ); if ( loadingPlayerIconCount < MAX_LOADING_PLAYER_ICONS ) { Q_strncpyz( model, Info_ValueForKey( info, "model" ), sizeof( model ) ); skin = strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = "default"; } Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", model, skin ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { Com_sprintf( iconName, MAX_QPATH, "models/players/characters/%s/icon_%s.tga", model, skin ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); } if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", DEFAULT_MODEL, "default" ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); } if ( loadingPlayerIcons[loadingPlayerIconCount] ) { loadingPlayerIconCount++; } } Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) ); Q_CleanStr( personality ); if( cgs.gametype == GT_SINGLE_PLAYER ) { trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue ); } CG_LoadingString( personality ); } /* ==================== CG_DrawInformation Draw all the status / pacifier stuff during level loading ==================== */ void CG_DrawInformation( void ) { const char *s; const char *info; const char *sysInfo; int y; int value; qhandle_t levelshot; qhandle_t detail; char buf[1024]; info = CG_ConfigString( CS_SERVERINFO ); sysInfo = CG_ConfigString( CS_SYSTEMINFO ); s = Info_ValueForKey( info, "mapname" ); levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) ); if ( !levelshot ) { levelshot = trap_R_RegisterShaderNoMip( "menu/art/unknownmap" ); } trap_R_SetColor( NULL ); CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot ); // blend a detail texture over it detail = trap_R_RegisterShader( "levelShotDetail" ); trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 2.5, 2, detail ); // draw the icons of things as they are loaded CG_DrawLoadingIcons(); // the first 150 rows are reserved for the client connection // screen to write into if ( cg.infoScreenText[0] ) { UI_DrawProportionalString( 320, 128-32, va("Loading... %s", cg.infoScreenText), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); } else { UI_DrawProportionalString( 320, 128-32, "Awaiting snapshot...", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); } // draw info string information y = 180-32; // don't print server lines if playing a local game trap_Cvar_VariableStringBuffer( "sv_running", buf, sizeof( buf ) ); if ( !atoi( buf ) ) { // server hostname Q_strncpyz(buf, Info_ValueForKey( info, "sv_hostname" ), 1024); Q_CleanStr(buf); UI_DrawProportionalString( 320, y, buf, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; // pure server s = Info_ValueForKey( sysInfo, "sv_pure" ); if ( s[0] == '1' ) { UI_DrawProportionalString( 320, y, "Pure Server", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // server-specific message of the day s = CG_ConfigString( CS_MOTD ); if ( s[0] ) { UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // some extra space after hostname and motd y += 10; } // map-specific message (long map name) s = CG_ConfigString( CS_MESSAGE ); if ( s[0] ) { UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // cheats warning s = Info_ValueForKey( sysInfo, "sv_cheats" ); if ( s[0] == '1' ) { UI_DrawProportionalString( 320, y, "CHEATS ARE ENABLED", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // game type switch ( cgs.gametype ) { case GT_FFA: s = "Free For All"; break; case GT_SINGLE_PLAYER: s = "Single Player"; break; case GT_TOURNAMENT: s = "Tournament"; break; case GT_TEAM: s = "Team Deathmatch"; break; case GT_CTF: s = "Capture The Flag"; break; //#ifdef MISSIONPACK case GT_1FCTF: s = "One Flag CTF"; break; case GT_OBELISK: s = "Overload"; break; case GT_HARVESTER: s = "Harvester"; break; //#endif case GT_ELIMINATION: s = "Elimination"; break; case GT_CTF_ELIMINATION: s = " CTF Elimination"; break; case GT_LMS: s = "Last Man Standing"; break; case GT_DOUBLE_D: s = "Double Domination"; break; case GT_DOMINATION: s = "Domination"; break; default: s = "Unknown Gametype"; break; } UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; value = atoi( Info_ValueForKey( info, "timelimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "timelimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } if (cgs.gametype < GT_CTF || cgs.ffa_gt>0) { value = atoi( Info_ValueForKey( info, "fraglimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "fraglimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } } if (cgs.gametype >= GT_CTF && cgs.ffa_gt == 0) { value = atoi( Info_ValueForKey( info, "capturelimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "capturelimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } } } openarena_0.8.8.orig/code/cgame/cg_public.h0000644000175000017500000001405411656310265017321 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #define CMD_BACKUP 64 #define CMD_MASK (CMD_BACKUP - 1) // allow a lot of command backups for very fast systems // multiple commands may be combined into a single packet, so this // needs to be larger than PACKET_BACKUP #define MAX_ENTITIES_IN_SNAPSHOT 256 // snapshots are a view of the server at a given time // Snapshots are generated at regular time intervals by the server, // but they may not be sent if a client's rate level is exceeded, or // they may be dropped by the network. typedef struct { int snapFlags; // SNAPFLAG_RATE_DELAYED, etc int ping; int serverTime; // server time the message is valid for (in msec) byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits playerState_t ps; // complete information about the current player at this time int numEntities; // all of the entities that need to be presented entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot int numServerCommands; // text based server commands to execute when this int serverCommandSequence; // snapshot becomes current } snapshot_t; enum { CGAME_EVENT_NONE, CGAME_EVENT_TEAMMENU, CGAME_EVENT_SCOREBOARD, CGAME_EVENT_EDITHUD }; /* ================================================================== functions imported from the main executable ================================================================== */ #define CGAME_IMPORT_API_VERSION 4 typedef enum { CG_PRINT, CG_ERROR, CG_MILLISECONDS, CG_CVAR_REGISTER, CG_CVAR_UPDATE, CG_CVAR_SET, CG_CVAR_VARIABLESTRINGBUFFER, CG_ARGC, CG_ARGV, CG_ARGS, CG_FS_FOPENFILE, CG_FS_READ, CG_FS_WRITE, CG_FS_FCLOSEFILE, CG_SENDCONSOLECOMMAND, CG_ADDCOMMAND, CG_SENDCLIENTCOMMAND, CG_UPDATESCREEN, CG_CM_LOADMAP, CG_CM_NUMINLINEMODELS, CG_CM_INLINEMODEL, CG_CM_LOADMODEL, CG_CM_TEMPBOXMODEL, CG_CM_POINTCONTENTS, CG_CM_TRANSFORMEDPOINTCONTENTS, CG_CM_BOXTRACE, CG_CM_TRANSFORMEDBOXTRACE, CG_CM_MARKFRAGMENTS, CG_S_STARTSOUND, CG_S_STARTLOCALSOUND, CG_S_CLEARLOOPINGSOUNDS, CG_S_ADDLOOPINGSOUND, CG_S_UPDATEENTITYPOSITION, CG_S_RESPATIALIZE, CG_S_REGISTERSOUND, CG_S_STARTBACKGROUNDTRACK, CG_R_LOADWORLDMAP, CG_R_REGISTERMODEL, CG_R_REGISTERSKIN, CG_R_REGISTERSHADER, CG_R_CLEARSCENE, CG_R_ADDREFENTITYTOSCENE, CG_R_ADDPOLYTOSCENE, CG_R_ADDLIGHTTOSCENE, CG_R_RENDERSCENE, CG_R_SETCOLOR, CG_R_DRAWSTRETCHPIC, CG_R_MODELBOUNDS, CG_R_LERPTAG, CG_GETGLCONFIG, CG_GETGAMESTATE, CG_GETCURRENTSNAPSHOTNUMBER, CG_GETSNAPSHOT, CG_GETSERVERCOMMAND, CG_GETCURRENTCMDNUMBER, CG_GETUSERCMD, CG_SETUSERCMDVALUE, CG_R_REGISTERSHADERNOMIP, CG_MEMORY_REMAINING, CG_R_REGISTERFONT, CG_KEY_ISDOWN, CG_KEY_GETCATCHER, CG_KEY_SETCATCHER, CG_KEY_GETKEY, CG_PC_ADD_GLOBAL_DEFINE, CG_PC_LOAD_SOURCE, CG_PC_FREE_SOURCE, CG_PC_READ_TOKEN, CG_PC_SOURCE_FILE_AND_LINE, CG_S_STOPBACKGROUNDTRACK, CG_REAL_TIME, CG_SNAPVECTOR, CG_REMOVECOMMAND, CG_R_LIGHTFORPOINT, CG_CIN_PLAYCINEMATIC, CG_CIN_STOPCINEMATIC, CG_CIN_RUNCINEMATIC, CG_CIN_DRAWCINEMATIC, CG_CIN_SETEXTENTS, CG_R_REMAP_SHADER, CG_S_ADDREALLOOPINGSOUND, CG_S_STOPLOOPINGSOUND, CG_CM_TEMPCAPSULEMODEL, CG_CM_CAPSULETRACE, CG_CM_TRANSFORMEDCAPSULETRACE, CG_R_ADDADDITIVELIGHTTOSCENE, CG_GET_ENTITY_TOKEN, CG_R_ADDPOLYSTOSCENE, CG_R_INPVS, // 1.32 CG_FS_SEEK, /* CG_LOADCAMERA, CG_STARTCAMERA, CG_GETCAMERAINFO, */ CG_MEMSET = 100, CG_MEMCPY, CG_STRNCPY, CG_SIN, CG_COS, CG_ATAN2, CG_SQRT, CG_FLOOR, CG_CEIL, CG_TESTPRINTINT, CG_TESTPRINTFLOAT, CG_ACOS } cgameImport_t; /* ================================================================== functions exported to the main executable ================================================================== */ typedef enum { CG_INIT, // void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) // called when the level loads or when the renderer is restarted // all media should be registered at this time // cgame will display loading status by calling SCR_Update, which // will call CG_DrawInformation during the loading process // reliableCommandSequence will be 0 on fresh loads, but higher for // demos, tourney restarts, or vid_restarts CG_SHUTDOWN, // void (*CG_Shutdown)( void ); // oportunity to flush and close any open files CG_CONSOLE_COMMAND, // qboolean (*CG_ConsoleCommand)( void ); // a console command has been issued locally that is not recognized by the // main game system. // use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the // command is not known to the game CG_DRAW_ACTIVE_FRAME, // void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); // Generates and draws a game scene and status information at the given time. // If demoPlayback is set, local movement prediction will not be enabled CG_CROSSHAIR_PLAYER, // int (*CG_CrosshairPlayer)( void ); CG_LAST_ATTACKER, // int (*CG_LastAttacker)( void ); CG_KEY_EVENT, // void (*CG_KeyEvent)( int key, qboolean down ); CG_MOUSE_EVENT, // void (*CG_MouseEvent)( int dx, int dy ); CG_EVENT_HANDLING // void (*CG_EventHandling)(int type); } cgameExport_t; //---------------------------------------------- openarena_0.8.8.orig/code/cgame/cg_scoreboard.c0000644000175000017500000003673011656310265020166 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_scoreboard -- draw the scoreboard on top of the game screen #include "cg_local.h" #define SCOREBOARD_X (0) #define SB_HEADER 86 #define SB_TOP (SB_HEADER+32) // Where the status bar starts, so we don't overwrite it #define SB_STATUSBAR 420 #define SB_NORMAL_HEIGHT 40 #define SB_INTER_HEIGHT 16 // interleaved height #define SB_MAXCLIENTS_NORMAL ((SB_STATUSBAR - SB_TOP) / SB_NORMAL_HEIGHT) #define SB_MAXCLIENTS_INTER ((SB_STATUSBAR - SB_TOP) / SB_INTER_HEIGHT - 1) // Used when interleaved #define SB_LEFT_BOTICON_X (SCOREBOARD_X+0) #define SB_LEFT_HEAD_X (SCOREBOARD_X+32) #define SB_RIGHT_BOTICON_X (SCOREBOARD_X+64) #define SB_RIGHT_HEAD_X (SCOREBOARD_X+96) // Normal #define SB_BOTICON_X (SCOREBOARD_X+32) #define SB_HEAD_X (SCOREBOARD_X+64) #define SB_SCORELINE_X 112 #define SB_RATING_WIDTH (6 * BIGCHAR_WIDTH) // width 6 #define SB_SCORE_X (SB_SCORELINE_X + BIGCHAR_WIDTH) // width 6 #define SB_RATING_X (SB_SCORELINE_X + 6 * BIGCHAR_WIDTH) // width 6 #define SB_PING_X (SB_SCORELINE_X + 12 * BIGCHAR_WIDTH + 8) // width 5 #define SB_TIME_X (SB_SCORELINE_X + 17 * BIGCHAR_WIDTH + 8) // width 5 #define SB_NAME_X (SB_SCORELINE_X + 22 * BIGCHAR_WIDTH) // width 15 // The new and improved score board // // In cases where the number of clients is high, the score board heads are interleaved // here's the layout // // 0 32 80 112 144 240 320 400 <-- pixel position // bot head bot head score ping time name // // wins/losses are drawn on bot icon now static qboolean localClient; // true if local client has been displayed /* ================= CG_DrawScoreboard ================= */ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, qboolean largeFormat ) { char string[1024]; vec3_t headAngles; clientInfo_t *ci; int iconx, headx; if ( score->client < 0 || score->client >= cgs.maxclients ) { Com_Printf( "Bad score->client: %i\n", score->client ); return; } ci = &cgs.clientinfo[score->client]; iconx = SB_BOTICON_X + (SB_RATING_WIDTH / 2); headx = SB_HEAD_X + (SB_RATING_WIDTH / 2); // draw the handicap or bot skill marker (unless player has flag) if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse ); } } else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse ); } } else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse ); } } else { if ( ci->botSkill > 0 && ci->botSkill <= 5 ) { if ( cg_drawIcons.integer ) { if( largeFormat ) { CG_DrawPic( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); } else { CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); } } } else if ( ci->handicap < 100 ) { Com_sprintf( string, sizeof( string ), "%i", ci->handicap ); if ( cgs.gametype == GT_TOURNAMENT ) CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color ); else CG_DrawSmallStringColor( iconx, y, string, color ); } // draw the wins / losses if ( cgs.gametype == GT_TOURNAMENT ) { Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses ); if( ci->handicap < 100 && !ci->botSkill ) { CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color ); } else { CG_DrawSmallStringColor( iconx, y, string, color ); } } } // draw the face VectorClear( headAngles ); headAngles[YAW] = 180; if( largeFormat ) { CG_DrawHead( headx, y - ( ICON_SIZE - BIGCHAR_HEIGHT ) / 2, ICON_SIZE, ICON_SIZE, score->client, headAngles ); } else { CG_DrawHead( headx, y, 16, 16, score->client, headAngles ); } #ifdef MISSIONPACK // draw the team task if ( ci->teamTask != TEAMTASK_NONE ) { if (ci->isDead) { CG_DrawPic( headx + 48, y, 16, 16, cgs.media.deathShader ); } else if ( ci->teamTask == TEAMTASK_OFFENSE ) { CG_DrawPic( headx + 48, y, 16, 16, cgs.media.assaultShader ); } else if ( ci->teamTask == TEAMTASK_DEFENSE ) { CG_DrawPic( headx + 48, y, 16, 16, cgs.media.defendShader ); } } #endif // draw the score line if ( score->ping == -1 ) { Com_sprintf(string, sizeof(string), " connecting %s", ci->name); } else if ( ci->team == TEAM_SPECTATOR ) { Com_sprintf(string, sizeof(string), " SPECT %3i %4i %s", score->ping, score->time, ci->name); } else { /*if(cgs.gametype == GT_LMS) Com_sprintf(string, sizeof(string), "%5i %4i %4i %s *%i*", score->score, score->ping, score->time, ci->name, ci->isDead); else*/ /*if(ci->isDead) Com_sprintf(string, sizeof(string), "%5i %4i %4i %s *DEAD*", score->score, score->ping, score->time, ci->name); else*/ Com_sprintf(string, sizeof(string), "%5i %4i %4i %s", score->score, score->ping, score->time, ci->name); } // highlight your position if ( score->client == cg.snap->ps.clientNum ) { float hcolor[4]; int rank; localClient = qtrue; if ( ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) || ( ( cgs.gametype >= GT_TEAM ) && ( cgs.ffa_gt != 1 ) ) ) { // Sago: I think this means that it doesn't matter if two players are tied in team game - only team score counts rank = -1; } else { rank = cg.snap->ps.persistant[PERS_RANK] & ~RANK_TIED_FLAG; } if ( rank == 0 ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 0.7f; } else if ( rank == 1 ) { hcolor[0] = 0.7f; hcolor[1] = 0; hcolor[2] = 0; } else if ( rank == 2 ) { hcolor[0] = 0.7f; hcolor[1] = 0.7f; hcolor[2] = 0; } else { hcolor[0] = 0.7f; hcolor[1] = 0.7f; hcolor[2] = 0.7f; } hcolor[3] = fade * 0.7; CG_FillRect( SB_SCORELINE_X + BIGCHAR_WIDTH + (SB_RATING_WIDTH / 2), y, 640 - SB_SCORELINE_X - BIGCHAR_WIDTH, BIGCHAR_HEIGHT+1, hcolor ); } CG_DrawBigString( SB_SCORELINE_X + (SB_RATING_WIDTH / 2), y, string, fade ); // add the "ready" marker for intermission exiting if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) { CG_DrawBigStringColor( iconx, y, "READY", color ); } else if(cgs.gametype == GT_LMS) { CG_DrawBigStringColor( iconx-50, y, va("*%i*",ci->isDead), color ); } else if(ci->isDead) { CG_DrawBigStringColor( iconx-60, y, "DEAD", color ); } } /* ================= CG_TeamScoreboard ================= */ static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, int lineHeight ) { int i; score_t *score; float color[4]; int count; clientInfo_t *ci; color[0] = color[1] = color[2] = 1.0; color[3] = fade; count = 0; for ( i = 0 ; i < cg.numScores && count < maxClients ; i++ ) { score = &cg.scores[i]; ci = &cgs.clientinfo[ score->client ]; if ( team != ci->team ) { continue; } CG_DrawClientScore( y + lineHeight * count, score, color, fade, lineHeight == SB_NORMAL_HEIGHT ); count++; } return count; } /* ================= CG_DrawScoreboard Draw the normal in-game scoreboard ================= */ qboolean CG_DrawOldScoreboard( void ) { int x, y, w, i, n1, n2; float fade; float *fadeColor; char *s; int maxClients; int lineHeight; int topBorderSize, bottomBorderSize; // don't draw amuthing if the menu or console is up if ( cg_paused.integer ) { cg.deferredPlayerLoading = 0; return qfalse; } if ( cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { cg.deferredPlayerLoading = 0; return qfalse; } // don't draw scoreboard during death while warmup up if ( cg.warmup && !cg.showScores ) { return qfalse; } if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { fade = 1.0; fadeColor = colorWhite; } else { fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); if ( !fadeColor ) { // next time scoreboard comes up, don't print killer cg.deferredPlayerLoading = 0; cg.killerName[0] = 0; return qfalse; } fade = *fadeColor; } // fragged by ... line if ( cg.killerName[0] ) { s = va("Fragged by %s", cg.killerName ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 40; CG_DrawBigString( x, y, s, fade ); } // current rank if ( cgs.gametype < GT_TEAM || cgs.ffa_gt == 1) { if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { s = va("%s place with %i", CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), cg.snap->ps.persistant[PERS_SCORE] ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 60; CG_DrawBigString( x, y, s, fade ); } } else { if ( cg.teamScores[0] == cg.teamScores[1] ) { s = va("Teams are tied at %i", cg.teamScores[0] ); } else if ( cg.teamScores[0] >= cg.teamScores[1] ) { s = va("Red leads %i to %i",cg.teamScores[0], cg.teamScores[1] ); } else { s = va("Blue leads %i to %i",cg.teamScores[1], cg.teamScores[0] ); } w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 60; CG_DrawBigString( x, y, s, fade ); } // scoreboard y = SB_HEADER; CG_DrawPic( SB_SCORE_X + (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardScore ); CG_DrawPic( SB_PING_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardPing ); CG_DrawPic( SB_TIME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardTime ); CG_DrawPic( SB_NAME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardName ); y = SB_TOP; // If there are more than SB_MAXCLIENTS_NORMAL, use the interleaved scores if ( cg.numScores > SB_MAXCLIENTS_NORMAL ) { maxClients = SB_MAXCLIENTS_INTER; lineHeight = SB_INTER_HEIGHT; topBorderSize = 8; bottomBorderSize = 16; } else { maxClients = SB_MAXCLIENTS_NORMAL; lineHeight = SB_NORMAL_HEIGHT; topBorderSize = 16; bottomBorderSize = 16; } localClient = qfalse; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { // // teamplay scoreboard // y += lineHeight/2; if ( cg.teamScores[0] >= cg.teamScores[1] ) { n1 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n1; n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n2; } else { n1 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n1; n2 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n2; } n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; } else { // // free for all scoreboard // n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; } if (!localClient) { // draw local client at the bottom for ( i = 0 ; i < cg.numScores ; i++ ) { if ( cg.scores[i].client == cg.snap->ps.clientNum ) { CG_DrawClientScore( y, &cg.scores[i], fadeColor, fade, lineHeight == SB_NORMAL_HEIGHT ); break; } } } // load any models that have been deferred if ( ++cg.deferredPlayerLoading > 10 ) { CG_LoadDeferredPlayers(); } return qtrue; } //================================================================================ /* ================ CG_CenterGiantLine ================ */ static void CG_CenterGiantLine( float y, const char *string ) { float x; vec4_t color; color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( string ) ); CG_DrawStringExt( x, y, string, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); } /* ================= CG_DrawTourneyScoreboard Draw the oversize scoreboard for tournements ================= */ void CG_DrawOldTourneyScoreboard( void ) { const char *s; vec4_t color; int min, tens, ones; clientInfo_t *ci; int y; int i; // request more scores regularly if ( cg.scoresRequestTime + 2000 < cg.time ) { cg.scoresRequestTime = cg.time; trap_SendClientCommand( "score" ); } // draw the dialog background color[0] = color[1] = color[2] = 0; color[3] = 1; CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color ); color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; // print the mesage of the day s = CG_ConfigString( CS_MOTD ); if ( !s[0] ) { s = "Scoreboard"; } // print optional title CG_CenterGiantLine( 8, s ); // print server time ones = cg.time / 1000; min = ones / 60; ones %= 60; tens = ones / 10; ones %= 10; s = va("%i:%i%i", min, tens, ones ); CG_CenterGiantLine( 64, s ); // print the two scores y = 160; if ( cgs.gametype >= GT_TEAM && cgs.ffa_gt!=1) { // // teamplay scoreboard // CG_DrawStringExt( 8, y, "Red Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", cg.teamScores[0] ); CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); y += 64; CG_DrawStringExt( 8, y, "Blue Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", cg.teamScores[1] ); CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); } else { // // free for all scoreboard // for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { ci = &cgs.clientinfo[i]; if ( !ci->infoValid ) { continue; } if ( ci->team != TEAM_FREE ) { continue; } CG_DrawStringExt( 8, y, ci->name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", ci->score ); CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); y += 64; } } } openarena_0.8.8.orig/code/cgame/cg_weapons.c0000644000175000017500000027736211656310265017527 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_weapons.c -- events and effects dealing with weapons #include "cg_local.h" /* ========================== CG_MachineGunEjectBrass ========================== */ static void CG_MachineGunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; float waterScale = 1.0f; vec3_t v[3]; if ( cg_brassTime.integer <= 0 ) { return; } le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 0; velocity[1] = -50 + 40 * crandom(); velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time - (rand()&15); AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = -4; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.machinegunBrassModel; le->bounceFactor = 0.4 * waterScale; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; } /* ========================== CG_ShotgunEjectBrass ========================== */ static void CG_ShotgunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; vec3_t v[3]; int i; if ( cg_brassTime.integer <= 0 ) { return; } for ( i = 0; i < 2; i++ ) { float waterScale = 1.0f; le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 + 60 * crandom(); if ( i == 0 ) { velocity[1] = 40 + 10 * crandom(); } else { velocity[1] = -40 + 10 * crandom(); } velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = 0; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.shotgunBrassModel; le->bounceFactor = 0.3f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_SHELL; // LEILEI shell noises le->leMarkType = LEMT_NONE; } } //#ifdef MISSIONPACK /* ========================== CG_NailgunEjectBrass ========================== */ static void CG_NailgunEjectBrass( centity_t *cent ) { localEntity_t *smoke; vec3_t origin; vec3_t v[3]; vec3_t offset; vec3_t xoffset; vec3_t up; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 0; offset[1] = -12; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, origin ); VectorSet( up, 0, 0, 64 ); smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } //#endif /* ========================== CG_RailTrail ========================== */ void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) { vec3_t axis[36], move, move2, next_move, vec, temp; float len; int i, j, skip; localEntity_t *le; refEntity_t *re; #define RADIUS 4 #define ROTATION 1 #define SPACING 5 start[2] -= 4; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FADE_RGB; le->startTime = cg.time; le->endTime = cg.time + cg_railTrailTime.value; le->lifeRate = 1.0 / (le->endTime - le->startTime); re->shaderTime = cg.time / 1000.0f; re->reType = RT_RAIL_CORE; re->customShader = cgs.media.railCoreShader; VectorCopy(start, re->origin); VectorCopy(end, re->oldorigin); re->shaderRGBA[0] = ci->color1[0] * 255; re->shaderRGBA[1] = ci->color1[1] * 255; re->shaderRGBA[2] = ci->color1[2] * 255; re->shaderRGBA[3] = 255; le->color[0] = ci->color1[0] * 0.75; le->color[1] = ci->color1[1] * 0.75; le->color[2] = ci->color1[2] * 0.75; le->color[3] = 1.0f; AxisClear( re->axis ); if (cg_oldRail.integer) { // nudge down a bit so it isn't exactly in center re->origin[2] -= 8; re->oldorigin[2] -= 8; return; } VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); PerpendicularVector(temp, vec); for (i = 0 ; i < 36; i++) { RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10 } VectorMA(move, 20, vec, move); VectorCopy(move, next_move); VectorScale (vec, SPACING, vec); skip = -1; j = 18; for (i = 0; i < len; i += SPACING) { if (i != skip) { skip = i + SPACING; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leFlags = LEF_PUFF_DONT_SCALE; le->leType = LE_MOVE_SCALE_FADE; le->startTime = cg.time; le->endTime = cg.time + (i>>1) + 600; le->lifeRate = 1.0 / (le->endTime - le->startTime); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 1.1f; re->customShader = cgs.media.railRingsShader; re->shaderRGBA[0] = ci->color2[0] * 255; re->shaderRGBA[1] = ci->color2[1] * 255; re->shaderRGBA[2] = ci->color2[2] * 255; re->shaderRGBA[3] = 255; le->color[0] = ci->color2[0] * 0.75; le->color[1] = ci->color2[1] * 0.75; le->color[2] = ci->color2[2] * 0.75; le->color[3] = 1.0f; le->pos.trType = TR_LINEAR; le->pos.trTime = cg.time; VectorCopy( move, move2); VectorMA(move2, RADIUS , axis[j], move2); VectorCopy(move2, le->pos.trBase); le->pos.trDelta[0] = axis[j][0]*6; le->pos.trDelta[1] = axis[j][1]*6; le->pos.trDelta[2] = axis[j][2]*6; } VectorAdd (move, vec, move); j = (j + ROTATION) % 36; } } /* ========================== CG_OldRocketTrail (for the crappy old rocket trail.) ========================== */ static void CG_OldRocketTrail( centity_t *ent, const weaponInfo_t *wi ) { int step; vec3_t origin, lastPos; int t; int startTime, contents; int lastContents; entityState_t *es; vec3_t up; localEntity_t *smoke; if ( cg_noProjectileTrail.integer ) { return; } up[0] = 0; up[1] = 0; up[2] = 0; step = 50; es = &ent->currentState; startTime = ent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); contents = CG_PointContents( origin, -1 ); // if object (e.g. grenade) is stationary, don't toss up smoke if ( es->pos.trType == TR_STATIONARY ) { ent->trailTime = cg.time; return; } BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); lastContents = CG_PointContents( lastPos, -1 ); ent->trailTime = cg.time; if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { if ( contents & lastContents & CONTENTS_WATER ) { CG_BubbleTrail( lastPos, origin, 8 ); } return; } for ( ; t <= ent->trailTime ; t += step ) { BG_EvaluateTrajectory( &es->pos, t, lastPos ); smoke = CG_SmokePuff( lastPos, up, wi->trailRadius, 1, 1, 1, 0.33f, wi->wiTrailTime, t, 0, 0, cgs.media.smokePuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } } /* ========================== CG_LeiSmokeTrail ========================== */ static void CG_LeiSmokeTrail( centity_t *ent, const weaponInfo_t *wi ) { int step; vec3_t origin, lastPos; int t; int startTime, contents; int lastContents; entityState_t *es; vec3_t up; localEntity_t *smoke; int therando; int theradio; if ( cg_noProjectileTrail.integer ) { return; } up[0] = 5 - 10 * crandom(); up[1] = 5 - 10 * crandom(); up[2] = 8 - 5 * crandom(); step = 18; es = &ent->currentState; startTime = ent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); contents = CG_PointContents( origin, -1 ); // if object (e.g. grenade) is stationary, don't toss up smoke if ( es->pos.trType == TR_STATIONARY ) { ent->trailTime = cg.time; return; } BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); lastContents = CG_PointContents( lastPos, -1 ); ent->trailTime = cg.time; if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { if ( contents & lastContents & CONTENTS_WATER ) { CG_BubbleTrail( lastPos, origin, 8 ); } return; } for ( ; t <= ent->trailTime ; t += step ) { BG_EvaluateTrajectory( &es->pos, t, lastPos ); therando = crandom() * 4; theradio = wi->trailRadius * (rand() * 0.7); // what is this doing here if (therando == 3) smoke = CG_SmokePuff( lastPos, up, 27, 1, 1, 1, 0.9f, wi->wiTrailTime, t, 0, 0, cgs.media.lsmkShader1 ); else if (therando == 1) smoke = CG_SmokePuff( lastPos, up, 27, 1, 1, 1, 0.9f, wi->wiTrailTime, t, 0, 0, cgs.media.lsmkShader2 ); else if (therando == 2) smoke = CG_SmokePuff( lastPos, up, 27, 1, 1, 1, 0.9f, wi->wiTrailTime, t, 0, 0, cgs.media.lsmkShader3 ); else smoke = CG_SmokePuff( lastPos, up, 27, 1, 1, 1, 0.9f, wi->wiTrailTime, t, 0, 0, cgs.media.lsmkShader4 ); // use the optimized local entity add smoke->leType = LE_MOVE_SCALE_FADE; //smoke->trType = TR_GRAVITY; } } static void CG_LeiPlasmaTrail( centity_t *ent, const weaponInfo_t *wi ) { int step; vec3_t origin, lastPos; int t; int startTime, contents; int lastContents; entityState_t *es; vec3_t up; localEntity_t *smoke; if ( cg_noProjectileTrail.integer ) { return; } up[0] = 0; up[1] = 0; up[2] = 0; step = 16; es = &ent->currentState; startTime = ent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); contents = CG_PointContents( origin, -1 ); // if object (e.g. grenade) is stationary, don't toss up smoke if ( es->pos.trType == TR_STATIONARY ) { ent->trailTime = cg.time; return; } BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); lastContents = CG_PointContents( lastPos, -1 ); ent->trailTime = cg.time; if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { return; } for ( ; t <= ent->trailTime ; t += step ) { BG_EvaluateTrajectory( &es->pos, t, lastPos ); smoke = CG_SmokePuff( lastPos, up, 27, 1, 1, 1, 0.9f, wi->wiTrailTime, t, 0, 0, cgs.media.lsmkShader1 ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; //smoke->trType = TR_GRAVITY; } } //#ifdef MISSIONPACK /* ========================== CG_NailTrail ========================== */ static void CG_NailTrail( centity_t *ent, const weaponInfo_t *wi ) { int step; vec3_t origin, lastPos; int t; int startTime, contents; int lastContents; entityState_t *es; vec3_t up; localEntity_t *smoke; if ( cg_noProjectileTrail.integer ) { return; } up[0] = 0; up[1] = 0; up[2] = 0; step = 50; es = &ent->currentState; startTime = ent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); contents = CG_PointContents( origin, -1 ); // if object (e.g. grenade) is stationary, don't toss up smoke if ( es->pos.trType == TR_STATIONARY ) { ent->trailTime = cg.time; return; } BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); lastContents = CG_PointContents( lastPos, -1 ); ent->trailTime = cg.time; if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { if ( contents & lastContents & CONTENTS_WATER ) { CG_BubbleTrail( lastPos, origin, 8 ); } return; } for ( ; t <= ent->trailTime ; t += step ) { BG_EvaluateTrajectory( &es->pos, t, lastPos ); smoke = CG_SmokePuff( lastPos, up, wi->trailRadius, 1, 1, 1, 0.33f, wi->wiTrailTime, t, 0, 0, cgs.media.nailPuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } } //#endif /* ========================== CG_NailTrail ========================== */ static void CG_OldPlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) { localEntity_t *le; refEntity_t *re; entityState_t *es; vec3_t velocity, xvelocity, origin; vec3_t offset, xoffset; vec3_t v[3]; int t, startTime, step; float waterScale = 1.0f; if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) { return; } step = 50; es = ¢->currentState; startTime = cent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 - 120 * crandom(); velocity[1] = 40 - 80 * crandom(); velocity[2] = 100 - 200 * crandom(); le->leType = LE_MOVE_SCALE_FADE; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_NONE; le->leMarkType = LEMT_NONE; le->startTime = cg.time; le->endTime = le->startTime + 600; le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 2; offset[1] = 2; offset[2] = 2; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( origin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 0.25f; re->customShader = cgs.media.railRingsShader; le->bounceFactor = 0.3f; re->shaderRGBA[0] = wi->flashDlightColor[0] * 63; re->shaderRGBA[1] = wi->flashDlightColor[1] * 63; re->shaderRGBA[2] = wi->flashDlightColor[2] * 63; re->shaderRGBA[3] = 63; le->color[0] = wi->flashDlightColor[0] * 0.2; le->color[1] = wi->flashDlightColor[1] * 0.2; le->color[2] = wi->flashDlightColor[2] * 0.2; le->color[3] = 0.25f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; } /* ========================== CG_GrappleTrail ========================== */ void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) { vec3_t origin; entityState_t *es; vec3_t forward, up; refEntity_t beam; es = &ent->currentState; BG_EvaluateTrajectory( &es->pos, cg.time, origin ); ent->trailTime = cg.time; memset( &beam, 0, sizeof( beam ) ); //FIXME adjust for muzzle position VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin ); beam.origin[2] += 26; AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up ); VectorMA( beam.origin, -6, up, beam.origin ); VectorCopy( origin, beam.oldorigin ); if (Distance( beam.origin, beam.oldorigin ) < 64 ) return; // Don't draw if close beam.reType = RT_RAIL_CORE; beam.customShader = cgs.media.grappleShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff; beam.shaderRGBA[2] = 0xff; beam.shaderRGBA[3] = 0xff; trap_R_AddRefEntityToScene( &beam ); } /* ========================== CG_GrenadeTrail ========================== */ // LEILEI enhancment static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) { if (cg_leiEnhancement.integer) { CG_LeiSmokeTrail( ent, wi ); } else { CG_OldRocketTrail( ent, wi ); } } static void CG_PlasmaTrail( centity_t *ent, const weaponInfo_t *wi ) { if (cg_leiEnhancement.integer) { CG_LeiPlasmaTrail( ent, wi ); } else { CG_OldPlasmaTrail( ent, wi ); } } static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) { CG_RocketTrail( ent, wi ); } /* ================= CG_RegisterWeapon The server says this item is used on this level ================= */ void CG_RegisterWeapon( int weaponNum ) { weaponInfo_t *weaponInfo; gitem_t *item, *ammo; char path[MAX_QPATH]; vec3_t mins, maxs; int i; weaponInfo = &cg_weapons[weaponNum]; if ( weaponNum == 0 ) { return; } if ( weaponInfo->registered ) { return; } memset( weaponInfo, 0, sizeof( *weaponInfo ) ); weaponInfo->registered = qtrue; for ( item = bg_itemlist + 1 ; item->classname ; item++ ) { if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) { weaponInfo->item = item; break; } } if ( !item->classname ) { CG_Error( "Couldn't find weapon %i", weaponNum ); } CG_RegisterItemVisuals( item - bg_itemlist ); // load cmodel before model so filecache works weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] ); // calc midpoint for rotation trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs ); for ( i = 0 ; i < 3 ; i++ ) { weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] ); } weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon ); weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon ); for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) { if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) { break; } } if ( ammo->classname && ammo->world_model[0] ) { weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] ); } Q_strncpyz( path, item->world_model[0], MAX_QPATH ); COM_StripExtension(path, path, sizeof(path)); strcat( path, "_flash.md3" ); weaponInfo->flashModel = trap_R_RegisterModel( path ); Q_strncpyz( path, item->world_model[0], MAX_QPATH ); COM_StripExtension(path, path, sizeof(path)); strcat( path, "_barrel.md3" ); weaponInfo->barrelModel = trap_R_RegisterModel( path ); Q_strncpyz( path, item->world_model[0], MAX_QPATH ); COM_StripExtension(path, path, sizeof(path)); strcat( path, "_hand.md3" ); weaponInfo->handsModel = trap_R_RegisterModel( path ); if ( !weaponInfo->handsModel ) { weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" ); } weaponInfo->loopFireSound = qfalse; switch ( weaponNum ) { case WP_GAUNTLET: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse ); break; case WP_LIGHTNING: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse ); weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse ); cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew"); cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" ); cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse ); cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav", qfalse ); cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav", qfalse ); break; case WP_GRAPPLING_HOOK: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/hook/hook.md3" ); weaponInfo->missileTrailFunc = CG_GrappleTrail; weaponInfo->missileDlight = 0; weaponInfo->wiTrailTime = 2000; weaponInfo->trailRadius = 64; MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); cgs.media.grappleShader = trap_R_RegisterShader( "grappleRope"); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grapple/grapfire.wav", qfalse ); weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/grapple/grappull.wav", qfalse ); //cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew"); break; //#ifdef MISSIONPACK case WP_CHAINGUN: weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/vulcan/wvulfire.wav", qfalse ); weaponInfo->loopFireSound = qtrue; MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf1b.wav", qfalse ); weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf2b.wav", qfalse ); weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf3b.wav", qfalse ); weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/vulcan/vulcanf4b.wav", qfalse ); weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" ); break; //#endif case WP_MACHINEGUN: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav", qfalse ); weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse ); weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse ); weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse ); weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" ); break; case WP_SHOTGUN: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse ); weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass; break; case WP_ROCKET_LAUNCHER: weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" ); weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); weaponInfo->missileTrailFunc = CG_RocketTrail; weaponInfo->missileDlight = 200; weaponInfo->wiTrailTime = 2000; weaponInfo->trailRadius = 64; MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" ); break; //#ifdef MISSIONPACK case WP_PROX_LAUNCHER: weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/proxmine.md3" ); weaponInfo->missileTrailFunc = CG_GrenadeTrail; weaponInfo->wiTrailTime = 700; weaponInfo->trailRadius = 32; MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/proxmine/wstbfire.wav", qfalse ); cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); break; //#endif case WP_GRENADE_LAUNCHER: weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" ); weaponInfo->missileTrailFunc = CG_GrenadeTrail; weaponInfo->wiTrailTime = 700; weaponInfo->trailRadius = 32; MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav", qfalse ); cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); break; //#ifdef MISSIONPACK case WP_NAILGUN: weaponInfo->ejectBrassFunc = CG_NailgunEjectBrass; weaponInfo->missileTrailFunc = CG_NailTrail; // weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/nailgun/wnalflit.wav", qfalse ); weaponInfo->trailRadius = 16; weaponInfo->wiTrailTime = 250; weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/nail.md3" ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/nailgun/wnalfire.wav", qfalse ); break; //#endif case WP_PLASMAGUN: // weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel; weaponInfo->missileTrailFunc = CG_PlasmaTrail; weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse ); cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" ); cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); break; case WP_RAILGUN: weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.5f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav", qfalse ); cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" ); cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" ); break; case WP_BFG: weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse ); cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" ); weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" ); weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); break; default: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); break; } } /* ================= CG_RegisterItemVisuals The server says this item is used on this level ================= */ void CG_RegisterItemVisuals( int itemNum ) { itemInfo_t *itemInfo; gitem_t *item; if ( itemNum < 0 || itemNum >= bg_numItems ) { CG_Error( "CG_RegisterItemVisuals: itemNum %d out of range [0-%d]", itemNum, bg_numItems-1 ); } itemInfo = &cg_items[ itemNum ]; if ( itemInfo->registered ) { return; } item = &bg_itemlist[ itemNum ]; memset( itemInfo, 0, sizeof( &itemInfo ) ); itemInfo->registered = qtrue; itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] ); itemInfo->icon = trap_R_RegisterShader( item->icon ); if ( item->giType == IT_WEAPON ) { CG_RegisterWeapon( item->giTag ); } // // powerups have an accompanying ring or sphere // if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH || item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) { if ( item->world_model[1] ) { itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] ); } } } /* ======================================================================================== VIEW WEAPON ======================================================================================== */ /* ================= CG_MapTorsoToWeaponFrame ================= */ static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) { // change weapon if ( frame >= ci->animations[TORSO_DROP].firstFrame && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) { return frame - ci->animations[TORSO_DROP].firstFrame + 6; } // stand attack if ( frame >= ci->animations[TORSO_ATTACK].firstFrame && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) { return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame; } // stand attack 2 if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) { return 1 + frame - ci->animations[TORSO_ATTACK2].firstFrame; } return 0; } /* ============== CG_CalculateWeaponPosition ============== */ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) { float scale; int delta; float fracsin; VectorCopy( cg.refdef.vieworg, origin ); VectorCopy( cg.refdefViewAngles, angles ); // on odd legs, invert some angles if ( cg.bobcycle & 1 ) { scale = -cg.xyspeed; } else { scale = cg.xyspeed; } // gun angles from bobbing angles[ROLL] += scale * cg.bobfracsin * 0.005; angles[YAW] += scale * cg.bobfracsin * 0.01; angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005; // drop the weapon when landing delta = cg.time - cg.landTime; if ( delta < LAND_DEFLECT_TIME ) { origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME; } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { origin[2] += cg.landChange*0.25 * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME; } #if 0 // drop the weapon when stair climbing delta = cg.time - cg.stepTime; if ( delta < STEP_TIME/2 ) { origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2); } else if ( delta < STEP_TIME ) { origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2); } #endif // idle drift scale = cg.xyspeed + 40; fracsin = sin( cg.time * 0.001 ); angles[ROLL] += scale * fracsin * 0.01; angles[YAW] += scale * fracsin * 0.01; angles[PITCH] += scale * fracsin * 0.01; } /* =============== CG_LightningBolt Origin will be the exact tag point, which is slightly different than the muzzle point used for determining hits. The cent should be the non-predicted cent if it is from the player, so the endpoint will reflect the simulated strike (lagging the predicted angle) =============== */ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { trace_t trace; refEntity_t beam; vec3_t forward; vec3_t muzzlePoint, endPoint; if (cent->currentState.weapon != WP_LIGHTNING) { return; } memset( &beam, 0, sizeof( beam ) ); //unlagged - attack prediction #1 // if the entity is us, unlagged is on server-side, and we've got it on for the lightning gun if ( (cent->currentState.number == cg.predictedPlayerState.clientNum) && cgs.delagHitscan && ( cg_delag.integer & 1 || cg_delag.integer & 8 ) ) { // always shoot straight forward from our current position AngleVectors( cg.predictedPlayerState.viewangles, forward, NULL, NULL ); VectorCopy( cg.predictedPlayerState.origin, muzzlePoint ); } else //unlagged - attack prediction #1 // CPMA "true" lightning if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) { vec3_t angle; int i; //unlagged - true lightning // might as well fix up true lightning while we're at it vec3_t viewangles; VectorCopy( cg.predictedPlayerState.viewangles, viewangles ); //unlagged - true lightning for (i = 0; i < 3; i++) { float a = cent->lerpAngles[i] - cg.refdefViewAngles[i]; if (a > 180) { a -= 360; } if (a < -180) { a += 360; } angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value); if (angle[i] < 0) { angle[i] += 360; } if (angle[i] > 360) { angle[i] -= 360; } } AngleVectors(angle, forward, NULL, NULL ); //unlagged - true lightning // VectorCopy(cent->lerpOrigin, muzzlePoint ); // VectorCopy(cg.refdef.vieworg, muzzlePoint ); // *this* is the correct origin for true lightning VectorCopy(cg.predictedPlayerState.origin, muzzlePoint ); //unlagged - true lightning } else { // !CPMA AngleVectors( cent->lerpAngles, forward, NULL, NULL ); VectorCopy(cent->lerpOrigin, muzzlePoint ); } // FIXME: crouch muzzlePoint[2] += DEFAULT_VIEWHEIGHT; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // project forward by the lightning range VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); // see if it hit a wall CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, MASK_SHOT ); // this is the endpoint VectorCopy( trace.endpos, beam.oldorigin ); // use the provided origin, even though it may be slightly // different than the muzzle origin VectorCopy( origin, beam.origin ); beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; trap_R_AddRefEntityToScene( &beam ); // add the impact flare if it hit something if ( trace.fraction < 1.0 ) { vec3_t angles; vec3_t dir; VectorSubtract( beam.oldorigin, beam.origin, dir ); VectorNormalize( dir ); memset( &beam, 0, sizeof( beam ) ); beam.hModel = cgs.media.lightningExplosionModel; VectorMA( trace.endpos, -16, dir, beam.origin ); // make a random orientation angles[0] = rand() % 360; angles[1] = rand() % 360; angles[2] = rand() % 360; AnglesToAxis( angles, beam.axis ); trap_R_AddRefEntityToScene( &beam ); } } /* static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { trace_t trace; refEntity_t beam; vec3_t forward; vec3_t muzzlePoint, endPoint; if ( cent->currentState.weapon != WP_LIGHTNING ) { return; } memset( &beam, 0, sizeof( beam ) ); // find muzzle point for this frame VectorCopy( cent->lerpOrigin, muzzlePoint ); AngleVectors( cent->lerpAngles, forward, NULL, NULL ); // FIXME: crouch muzzlePoint[2] += DEFAULT_VIEWHEIGHT; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // project forward by the lightning range VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); // see if it hit a wall CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, MASK_SHOT ); // this is the endpoint VectorCopy( trace.endpos, beam.oldorigin ); // use the provided origin, even though it may be slightly // different than the muzzle origin VectorCopy( origin, beam.origin ); beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; trap_R_AddRefEntityToScene( &beam ); // add the impact flare if it hit something if ( trace.fraction < 1.0 ) { vec3_t angles; vec3_t dir; VectorSubtract( beam.oldorigin, beam.origin, dir ); VectorNormalize( dir ); memset( &beam, 0, sizeof( beam ) ); beam.hModel = cgs.media.lightningExplosionModel; VectorMA( trace.endpos, -16, dir, beam.origin ); // make a random orientation angles[0] = rand() % 360; angles[1] = rand() % 360; angles[2] = rand() % 360; AnglesToAxis( angles, beam.axis ); trap_R_AddRefEntityToScene( &beam ); } } */ /* =============== CG_SpawnRailTrail Origin will be the exact tag point, which is slightly different than the muzzle point used for determining hits. =============== */ static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) { clientInfo_t *ci; if ( cent->currentState.weapon != WP_RAILGUN ) { return; } if ( !cent->pe.railgunFlash ) { return; } cent->pe.railgunFlash = qtrue; ci = &cgs.clientinfo[ cent->currentState.clientNum ]; CG_RailTrail( ci, origin, cent->pe.railgunImpact ); } /* ====================== CG_MachinegunSpinAngle ====================== */ #define SPIN_SPEED 0.9 #define COAST_TIME 1000 static float CG_MachinegunSpinAngle( centity_t *cent ) { int delta; float angle; float speed; delta = cg.time - cent->pe.barrelTime; if ( cent->pe.barrelSpinning ) { angle = cent->pe.barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = cent->pe.barrelAngle + delta * speed; } if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) { cent->pe.barrelTime = cg.time; cent->pe.barrelAngle = AngleMod( angle ); cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING); //#ifdef MISSIONPACK if ( cent->currentState.weapon == WP_CHAINGUN && !cent->pe.barrelSpinning ) { trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, trap_S_RegisterSound( "sound/weapons/vulcan/wvulwind.wav", qfalse ) ); } //#endif } return angle; } /* ======================== CG_AddWeaponWithPowerups ======================== */ static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) { // add powerup effects if ( powerups & ( 1 << PW_INVIS ) ) { if( (cgs.dmflags & DF_INVIS) == 0) { gun->customShader = cgs.media.invisShader; trap_R_AddRefEntityToScene( gun ); } } else { trap_R_AddRefEntityToScene( gun ); if ( powerups & ( 1 << PW_BATTLESUIT ) ) { gun->customShader = cgs.media.battleWeaponShader; trap_R_AddRefEntityToScene( gun ); } if ( powerups & ( 1 << PW_QUAD ) ) { gun->customShader = cgs.media.quadWeaponShader; trap_R_AddRefEntityToScene( gun ); } } } /* ============= CG_AddPlayerWeapon Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL) The main player will have this called for BOTH cases, so effects like light and sound should only be done on the world model case. ============= */ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ) { refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t angles; weapon_t weaponNum; weaponInfo_t *weapon; centity_t *nonPredictedCent; orientation_t lerped; weaponNum = cent->currentState.weapon; CG_RegisterWeapon( weaponNum ); weapon = &cg_weapons[weaponNum]; // add the weapon memset( &gun, 0, sizeof( gun ) ); VectorCopy( parent->lightingOrigin, gun.lightingOrigin ); gun.shadowPlane = parent->shadowPlane; gun.renderfx = parent->renderfx; // set custom shading for railgun refire rate if ( ps || cent->currentState.clientNum == cg.predictedPlayerState.clientNum ) { if ( cg.predictedPlayerState.weapon == WP_RAILGUN && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) { float f; f = (float)cg.predictedPlayerState.weaponTime / 1500; gun.shaderRGBA[1] = 0; gun.shaderRGBA[0] = gun.shaderRGBA[2] = 255 * ( 1.0 - f ); } else { gun.shaderRGBA[0] = 255; gun.shaderRGBA[1] = 255; gun.shaderRGBA[2] = 255; gun.shaderRGBA[3] = 255; } } gun.hModel = weapon->weaponModel; if (!gun.hModel) { return; } if ( !ps ) { // add weapon ready sound cent->pe.lightningFiring = qfalse; if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) { // lightning gun and guantlet make a different sound when fire is held down trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound ); cent->pe.lightningFiring = qtrue; } else if ( weapon->readySound ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound ); } } trap_R_LerpTag(&lerped, parent->hModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, "tag_weapon"); VectorCopy(parent->origin, gun.origin); VectorMA(gun.origin, lerped.origin[0], parent->axis[0], gun.origin); // Make weapon appear left-handed for 2 and centered for 3 if(ps && cg_drawGun.integer == 2) VectorMA(gun.origin, -lerped.origin[1], parent->axis[1], gun.origin); else if(!ps || cg_drawGun.integer != 3) VectorMA(gun.origin, lerped.origin[1], parent->axis[1], gun.origin); VectorMA(gun.origin, lerped.origin[2], parent->axis[2], gun.origin); MatrixMultiply(lerped.axis, ((refEntity_t *)parent)->axis, gun.axis); gun.backlerp = parent->backlerp; CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups ); // add the spinning barrel if ( weapon->barrelModel ) { memset( &barrel, 0, sizeof( barrel ) ); VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); barrel.shadowPlane = parent->shadowPlane; barrel.renderfx = parent->renderfx; barrel.hModel = weapon->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = CG_MachinegunSpinAngle( cent ); AnglesToAxis( angles, barrel.axis ); CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" ); CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); } // make sure we aren't looking at cg.predictedPlayerEntity for LG nonPredictedCent = &cg_entities[cent->currentState.clientNum]; // if the index of the nonPredictedCent is not the same as the clientNum // then this is a fake player (like on teh single player podiums), so // go ahead and use the cent if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { nonPredictedCent = cent; } // add the flash if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK ) && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) { // continuous flash } else { // impulse flash if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) { return; } } memset( &flash, 0, sizeof( flash ) ); VectorCopy( parent->lightingOrigin, flash.lightingOrigin ); flash.shadowPlane = parent->shadowPlane; flash.renderfx = parent->renderfx; flash.hModel = weapon->flashModel; if (!flash.hModel) { return; } angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = crandom() * 10; AnglesToAxis( angles, flash.axis ); // colorize the railgun blast if ( weaponNum == WP_RAILGUN ) { clientInfo_t *ci; ci = &cgs.clientinfo[ cent->currentState.clientNum ]; flash.shaderRGBA[0] = 255 * ci->color1[0]; flash.shaderRGBA[1] = 255 * ci->color1[1]; flash.shaderRGBA[2] = 255 * ci->color1[2]; } CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash"); trap_R_AddRefEntityToScene( &flash ); if ( ps || cg.renderingThirdPerson || cent->currentState.number != cg.predictedPlayerState.clientNum ) { // add lightning bolt CG_LightningBolt( nonPredictedCent, flash.origin ); // add rail trail CG_SpawnRailTrail( cent, flash.origin ); if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 300 + (rand()&31), weapon->flashDlightColor[0], weapon->flashDlightColor[1], weapon->flashDlightColor[2] ); } } } /* ============== CG_AddViewWeapon Add the weapon, and flash for the player's view ============== */ void CG_AddViewWeapon( playerState_t *ps ) { refEntity_t hand; centity_t *cent; clientInfo_t *ci; float fovOffset; vec3_t angles; weaponInfo_t *weapon; if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { return; } if ( ps->pm_type == PM_INTERMISSION ) { return; } // no gun if in third person view or a camera is active //if ( cg.renderingThirdPerson || cg.cameraMode) { if ( cg.renderingThirdPerson ) { return; } // allow the gun to be completely removed if ( !cg_drawGun.integer ) { vec3_t origin; if ( cg.predictedPlayerState.eFlags & EF_FIRING ) { // special hack for lightning gun... VectorCopy( cg.refdef.vieworg, origin ); VectorMA( origin, -8, cg.refdef.viewaxis[2], origin ); CG_LightningBolt( &cg_entities[ps->clientNum], origin ); } return; } // don't draw if testing a gun model if ( cg.testGun ) { return; } // drop gun lower at higher fov if ( cg_fov.integer > 90 ) { fovOffset = -0.2 * ( cg_fov.integer - 90 ); } else { fovOffset = 0; } cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum]; CG_RegisterWeapon( ps->weapon ); weapon = &cg_weapons[ ps->weapon ]; memset (&hand, 0, sizeof(hand)); // set up gun position CG_CalculateWeaponPosition( hand.origin, angles ); VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin ); VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin ); VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin ); AnglesToAxis( angles, hand.axis ); // map torso animations to weapon animations if ( cg_gun_frame.integer ) { // development tool hand.frame = hand.oldframe = cg_gun_frame.integer; hand.backlerp = 0; } else { // get clientinfo for animation map ci = &cgs.clientinfo[ cent->currentState.clientNum ]; hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame ); hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame ); hand.backlerp = cent->pe.torso.backlerp; } hand.hModel = weapon->handsModel; hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; // add everything onto the hand CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM] ); } /* ============================================================================== WEAPON SELECTION ============================================================================== */ /* =================== CG_DrawWeaponSelect =================== */ void CG_DrawWeaponSelect( void ) { int i; int bits; int count; float *color; vec4_t realColor; // don't display if dead if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return; } color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME ); //Elimination: Always show weapon bar if(cg_alwaysWeaponBar.integer) { realColor[0] = 1.0; realColor[1] = 1.0; realColor[2] = 1.0; realColor[3] = 1.0; color = realColor; } if ( !color ) { return; } trap_R_SetColor( color ); // showing weapon select clears pickup item display, but not the blend blob cg.itemPickupTime = 0; // count the number of weapons owned bits = cg.snap->ps.stats[ STAT_WEAPONS ]; count = 0; for ( i = 1 ; i < MAX_WEAPONS ; i++ ) { if ( bits & ( 1 << i ) ) { count++; } } switch(cg_weaponBarStyle.integer){ case 0: CG_DrawWeaponBar0(count,bits); break; case 1: CG_DrawWeaponBar1(count,bits); break; case 2: CG_DrawWeaponBar2(count,bits, color); break; case 3: CG_DrawWeaponBar3(count,bits, color); break; case 4: CG_DrawWeaponBar4(count,bits, color); break; case 5: CG_DrawWeaponBar5(count,bits, color); break; case 6: CG_DrawWeaponBar6(count,bits, color); break; case 7: CG_DrawWeaponBar7(count,bits, color); break; } trap_R_SetColor(NULL); return; } /* =============== CG_DrawWeaponBar0 =============== */ void CG_DrawWeaponBar0(int count, int bits){ int y = 380; int x = 320 - count * 20; int i; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon ); // draw selection marker if ( i == cg.weaponSelect ) { CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader ); } // no ammo cross on top if ( !cg.snap->ps.ammo[ i ] ) { CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader ); } x += 40; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar1 =============== */ void CG_DrawWeaponBar1(int count, int bits){ int y = 380; int x = 320 - count * 20; int i; int ammo; int br; int max; float red[4]; float yellow[4]; float green[4]; red[0] = 1.0f; red[1] = 0; red[2] = 0; red[3] = 1.0f; yellow[0] = 1.0f; yellow[1] = 0.6f; yellow[2] = 0; yellow[3] = 1.0f; green[0] = 0; green[1] = 1.0f; green[2] = 0; green[3] = 1.0f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } ammo=cg.snap->ps.ammo[i]; switch(i) { case WP_MACHINEGUN: max = 100; break; case WP_SHOTGUN: max = 10; break; case WP_GRENADE_LAUNCHER: max = 10; break; case WP_ROCKET_LAUNCHER: max = 10; break; case WP_LIGHTNING: max = 100; break; case WP_RAILGUN: max = 10; break; case WP_PLASMAGUN: max = 50; break; case WP_BFG: max = 10; break; case WP_NAILGUN: max = 10; break; case WP_PROX_LAUNCHER: max = 5; break; case WP_CHAINGUN: max = 100; break; default: max = 1; break; } ammo = (ammo*100)/max; if(ammo >=100) ammo=100; br=ammo*32/100; if(i!=WP_GAUNTLET && i!=WP_GRAPPLING_HOOK){ if(ammo <= 20) CG_FillRect( x, y+38, br,4, red); if(ammo > 20 && ammo <= 50) CG_FillRect( x, y+38, br, 4, yellow); if(ammo > 50) CG_FillRect( x, y+38, br, 4, green); } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon ); // draw selection marker if ( i == cg.weaponSelect ) { CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader ); } // no ammo cross on top if ( !cg.snap->ps.ammo[ i ] ) { CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader ); } x += 40; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar2 =============== */ void CG_DrawWeaponBar2(int count, int bits, float *color){ int y = 200 + count * 12; int x = 0; int i; int w; char *s; float red[4]; float yellow[4]; float blue[4]; red[0] = 1.0f; red[1] = 0; red[2] = 0; red[3] = 0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; blue[0] = 0; blue[1] = 0; blue[2] = 1.0f; blue[3] = 0.4f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } if(cg.snap->ps.ammo[i]){ if ( i == cg.weaponSelect) { CG_FillRect( x, y, 50, 24, blue ); CG_DrawRect( x, y, 50, 24, 2, yellow); } else{ CG_FillRect( x, y,50, 24, blue ); } } else{ if ( i == cg.weaponSelect) { CG_FillRect( x, y, 50, 24, red ); CG_DrawRect( x, y, 50, 24, 2, yellow); } else{ CG_FillRect( x, y,50, 24, red ); } } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x+2, y+4, 16, 16, cg_weapons[i].weaponIcon ); /** Draw Weapon Ammo **/ if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 32, y+4, s, color); } y -= 24; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar3 =============== */ void CG_DrawWeaponBar3(int count, int bits, float *color){ int y = 200 + count * 12; int x = 0; int i; int ammo; int max; int br; int w; char *s; float red[4]; float yellow[4]; float green[4]; float blue[4]; red[0] = 1.0f; red[1] = 0; red[2] = 0; red[3] = 0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; green[0] = 0; green[1] = 1.0f; green[2] = 0; green[3] = 1.0f; blue[0] = 0; blue[1] = 0; blue[2] = 1.0f; blue[3] = 0.4f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } ammo=cg.snap->ps.ammo[i]; switch(i) { case WP_MACHINEGUN: max = 100; break; case WP_SHOTGUN: max = 10; break; case WP_GRENADE_LAUNCHER: max = 10; break; case WP_ROCKET_LAUNCHER: max = 10; break; case WP_LIGHTNING: max = 100; break; case WP_RAILGUN: max = 10; break; case WP_PLASMAGUN: max = 50; break; case WP_BFG: max = 10; break; case WP_NAILGUN: max = 10; break; case WP_PROX_LAUNCHER: max = 5; break; case WP_CHAINGUN: max = 100; break; default: max = 1; break; } ammo = (ammo*100)/max; if(ammo >=100) ammo=100; br=ammo*20/100; if(i!=WP_GAUNTLET && i!=WP_GRAPPLING_HOOK){ if(ammo <= 20) CG_FillRect( 51, y+2+20-br, 4,br, red); if(ammo > 20 && ammo <= 50) CG_FillRect( 51, y+2+20-br, 4,br, yellow); if(ammo > 50) CG_FillRect( 51, y+2+20-br, 4,br, green); } if(cg.snap->ps.ammo[i]){ if ( i == cg.weaponSelect) { CG_FillRect( x, y, 50, 24, blue ); CG_DrawRect( x, y, 50, 24, 2, yellow); } else{ CG_FillRect( x, y,50, 24, blue ); } } else{ if ( i == cg.weaponSelect) { CG_FillRect( x, y, 50, 24, red ); CG_DrawRect( x, y, 50, 24, 2, yellow); } else{ CG_FillRect( x, y,50, 24, red ); } } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x+2, y+4, 16, 16, cg_weapons[i].weaponIcon ); /** Draw Weapon Ammo **/ if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 32, y+4, s, color); } y -= 24; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar4 =============== */ void CG_DrawWeaponBar4(int count, int bits, float *color){ int y = 200 + count * 12; int x = 0; int i; float ammo; int max; int w; char *s; float boxColor[4]; float yellow[4]; boxColor[1]=0; boxColor[3]=0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } ammo=cg.snap->ps.ammo[i]; switch(i) { case WP_MACHINEGUN: max = 100; break; case WP_SHOTGUN: max = 10; break; case WP_GRENADE_LAUNCHER: max = 10; break; case WP_ROCKET_LAUNCHER: max = 10; break; case WP_LIGHTNING: max = 100; break; case WP_RAILGUN: max = 10; break; case WP_PLASMAGUN: max = 50; break; case WP_BFG: max = 10; break; case WP_NAILGUN: max = 10; break; case WP_PROX_LAUNCHER: max = 5; break; case WP_CHAINGUN: max = 100; break; default: max = 1; break; } ammo = (ammo*100)/max; if((ammo >=100) || (ammo < 0)) ammo=100; boxColor[2]=(ammo/100.0f)*1.0f; boxColor[0]=1.0f-(ammo/100.0f)*1.0f; if ( i == cg.weaponSelect) { CG_FillRect( x, y, 50, 24, boxColor ); CG_DrawRect( x, y, 50, 24, 2, yellow); } else{ CG_FillRect( x, y,50, 24, boxColor ); } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x+2, y+4, 16, 16, cg_weapons[i].weaponIcon ); /** Draw Weapon Ammo **/ if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 32, y+4, s, color); } y -= 24; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar5 =============== */ void CG_DrawWeaponBar5(int count, int bits, float *color){ int y = 380; int x = 320 - count * 15; int i; int w; char *s; float red[4]; float yellow[4]; float blue[4]; red[0] = 1.0f; red[1] = 0; red[2] = 0; red[3] = 0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; blue[0] = 0; blue[1] = 0; blue[2] = 1.0f; blue[3] = 0.4f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } if(cg.snap->ps.ammo[i]){ if ( i == cg.weaponSelect) { CG_FillRect( x, y , 30 , 38, blue ); CG_DrawRect( x, y, 30 ,38 ,2, yellow); } else{ CG_FillRect( x, y,30, 38, blue ); } } else{ if ( i == cg.weaponSelect) { CG_FillRect( x, y , 30 , 38, red ); CG_DrawRect( x , y, 30,38,2, yellow); } else{ CG_FillRect( x, y,30, 38, red ); } } CG_RegisterWeapon( i ); CG_DrawPic( x+7, y+2, 16, 16, cg_weapons[i].weaponIcon ); if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 15, y+20, s, color); } x += 30; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar6 =============== */ void CG_DrawWeaponBar6(int count, int bits, float *color){ int y = 380; int x = 320 - count * 15; int i; int ammo; int max; int br; int w; char *s; float red[4]; float yellow[4]; float green[4]; float blue[4]; red[0] = 1.0f; red[1] = 0; red[2] = 0; red[3] = 0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; green[0] = 0; green[1] = 1.0f; green[2] = 0; green[3] = 1.0f; blue[0] = 0; blue[1] = 0; blue[2] = 1.0f; blue[3] = 0.4f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } ammo=cg.snap->ps.ammo[i]; switch(i) { case WP_MACHINEGUN: max = 100; break; case WP_SHOTGUN: max = 10; break; case WP_GRENADE_LAUNCHER: max = 10; break; case WP_ROCKET_LAUNCHER: max = 10; break; case WP_LIGHTNING: max = 100; break; case WP_RAILGUN: max = 10; break; case WP_PLASMAGUN: max = 50; break; case WP_BFG: max = 10; break; case WP_NAILGUN: max = 10; break; case WP_PROX_LAUNCHER: max = 5; break; case WP_CHAINGUN: max = 100; break; default: max = 1; break; } ammo = (ammo*100)/max; if(ammo >=100) ammo=100; br=ammo*26/100; if(i!=WP_GAUNTLET && i!=WP_GRAPPLING_HOOK){ if(ammo <= 20) CG_FillRect( x+2, y +40, br, 4, red); if(ammo > 20 && ammo <= 50) CG_FillRect( x+2, y+40, br, 4, yellow); if(ammo > 50) CG_FillRect( x+2, y+40, br, 4, green); } if(cg.snap->ps.ammo[i]){ if ( i == cg.weaponSelect) { CG_FillRect( x, y , 30 , 38, blue ); CG_DrawRect( x, y, 30 ,38 ,2, yellow); } else{ CG_FillRect( x, y,30, 38, blue ); } } else{ if ( i == cg.weaponSelect) { CG_FillRect( x, y , 30 , 38, red ); CG_DrawRect( x , y, 30,38,2, yellow); } else{ CG_FillRect( x, y,30, 38, red ); } } CG_RegisterWeapon( i ); CG_DrawPic( x+7, y+2, 16, 16, cg_weapons[i].weaponIcon ); if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 15, y+20, s, color); } x += 30; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_DrawWeaponBar7 =============== */ void CG_DrawWeaponBar7(int count, int bits, float *color){ int y = 380; int x = 320 - count * 15; int i; float ammo; float max; int w; char *s; float yellow[4]; float boxColor[4]; boxColor[1]=0; boxColor[3]=0.4f; yellow[0] = 1.0f; yellow[1] = 1.0f; yellow[2] = 0; yellow[3] = 1.0f; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { //Sago: Do mad change of grapple placement: if(i==10) continue; if(i==0) i=10; if ( !( bits & ( 1 << i ) ) ) { if(i==10) i=0; continue; } ammo=cg.snap->ps.ammo[i]; switch(i) { case WP_MACHINEGUN: max = 100; break; case WP_SHOTGUN: max = 10; break; case WP_GRENADE_LAUNCHER: max = 10; break; case WP_ROCKET_LAUNCHER: max = 10; break; case WP_LIGHTNING: max = 100; break; case WP_RAILGUN: max = 10; break; case WP_PLASMAGUN: max = 50; break; case WP_BFG: max = 10; break; case WP_NAILGUN: max = 10; break; case WP_PROX_LAUNCHER: max = 5; break; case WP_CHAINGUN: max = 100; break; default: max = 1; break; } ammo = (ammo*100)/max; if((ammo >=100) || (ammo < 0)) ammo=100; boxColor[2]=(ammo/100.0f)*1.0f; boxColor[0]=1.0f-(ammo/100.0f)*1.0f; if ( i == cg.weaponSelect) { CG_FillRect( x, y , 30 , 38, boxColor ); CG_DrawRect( x, y, 30 ,38 ,2, yellow); } else{ CG_FillRect( x, y,30, 38, boxColor ); } CG_RegisterWeapon( i ); CG_DrawPic( x+7, y+2, 16, 16, cg_weapons[i].weaponIcon ); if(cg.snap->ps.ammo[ i ]!=-1){ s = va("%i", cg.snap->ps.ammo[ i ] ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor(x - w/2 + 15, y+20, s, color); } x += 30; //Sago: Undo mad change of weapons if(i==10) i=0; } } /* =============== CG_WeaponSelectable =============== */ static qboolean CG_WeaponSelectable( int i ) { if ( !cg.snap->ps.ammo[i] ) { return qfalse; } if ( ! (cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ) ) ) { return qfalse; } return qtrue; } /* =============== CG_NextWeapon_f =============== */ void CG_NextWeapon_f( void ) { int i; int original; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } cg.weaponSelectTime = cg.time; original = cg.weaponSelect; //Part of mad hook select code: if(cg.weaponSelect == WP_GRAPPLING_HOOK) cg.weaponSelect = 0; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { cg.weaponSelect++; if ( cg.weaponSelect == MAX_WEAPONS ) { cg.weaponSelect = 0; } if ( cg.weaponSelect == WP_GAUNTLET ) { continue; // never cycle to gauntlet } //Sago: Mad change of grapple order if( cg.weaponSelect == WP_GRAPPLING_HOOK) { continue; } if( cg.weaponSelect == 0) cg.weaponSelect = WP_GRAPPLING_HOOK; if ( cg.weaponSelect == WP_GRAPPLING_HOOK && !cg_cyclegrapple.integer ) { cg.weaponSelect = 0; continue; // never cycle to grapple unless the client wants it } if ( CG_WeaponSelectable( cg.weaponSelect ) ) { break; } if( cg.weaponSelect == WP_GRAPPLING_HOOK) cg.weaponSelect = 0; } if ( i == MAX_WEAPONS ) { cg.weaponSelect = original; } } /* =============== CG_PrevWeapon_f =============== */ void CG_PrevWeapon_f( void ) { int i; int original; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } cg.weaponSelectTime = cg.time; original = cg.weaponSelect; //Part of mad hook select code: if(cg.weaponSelect == WP_GRAPPLING_HOOK) cg.weaponSelect = 0; for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { cg.weaponSelect--; if ( cg.weaponSelect == -1 ) { cg.weaponSelect = MAX_WEAPONS - 1; } if ( cg.weaponSelect == WP_GAUNTLET ) { continue; // never cycle to gauntlet } //Sago: Mad change of grapple order if( cg.weaponSelect == WP_GRAPPLING_HOOK) { continue; } if( cg.weaponSelect == 0) cg.weaponSelect = WP_GRAPPLING_HOOK; if ( cg.weaponSelect == WP_GRAPPLING_HOOK && !cg_cyclegrapple.integer ) { cg.weaponSelect = 0; continue; // never cycle to grapple unless the client wants it } if ( CG_WeaponSelectable( cg.weaponSelect ) ) { break; } if( cg.weaponSelect == WP_GRAPPLING_HOOK) cg.weaponSelect = 0; } if ( i == MAX_WEAPONS ) { cg.weaponSelect = original; } } /* =============== CG_Weapon_f =============== */ void CG_Weapon_f( void ) { int num; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } num = atoi( CG_Argv( 1 ) ); if ( num < 1 || num > MAX_WEAPONS-1 ) { return; } cg.weaponSelectTime = cg.time; if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) { return; // don't have the weapon } cg.weaponSelect = num; } /* =================== CG_OutOfAmmoChange The current weapon has just run out of ammo =================== */ void CG_OutOfAmmoChange( void ) { int i; cg.weaponSelectTime = cg.time; for ( i = MAX_WEAPONS-1 ; i > 0 ; i-- ) { if ( CG_WeaponSelectable( i ) && i != WP_GRAPPLING_HOOK ) { cg.weaponSelect = i; break; } } } /* =================================================================================================== WEAPON EVENTS =================================================================================================== */ /* ================ CG_FireWeapon Caused by an EV_FIRE_WEAPON event ================ */ void CG_FireWeapon( centity_t *cent ) { entityState_t *ent; int c; weaponInfo_t *weap; if((cgs.gametype == GT_ELIMINATION || cgs.gametype == GT_CTF_ELIMINATION) && cgs.roundStartTime>=cg.time) return; //if we havn't started in ELIMINATION then do not fire ent = ¢->currentState; if ( ent->weapon == WP_NONE ) { return; } if ( ent->weapon >= WP_NUM_WEAPONS ) { CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" ); return; } weap = &cg_weapons[ ent->weapon ]; // mark the entity as muzzle flashing, so when it is added it will // append the flash to the weapon model cent->muzzleFlashTime = cg.time; // lightning gun only does this this on initial press if ( ent->weapon == WP_LIGHTNING ) { if ( cent->pe.lightningFiring ) { return; } } // play quad sound if needed if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) { trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound ); } // play a sound for ( c = 0 ; c < 4 ; c++ ) { if ( !weap->flashSound[c] ) { break; } } if ( c > 0 ) { c = rand() % c; if ( weap->flashSound[c] ) { trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] ); } } // do brass ejection if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) { weap->ejectBrassFunc( cent ); } //unlagged - attack prediction #1 CG_PredictWeaponEffects( cent ); //unlagged - attack prediction #1 } /* ========================== CG_Explosionia LEILEI ========================== static void CG_Explosionia ( centity_t *cent ) { localEntity_t *le; ec3_t velocity, xvelocity; vec3_t offset, xoffset; float waterScale = 1.0f; vec3_t v[3]; if ( cg_brassTime.integer <= 0 ) { return; } le = CG_AllocLocalEntity(); velocity[0] = -50 + 100 * crandom(); velocity[1] = -50 + 100 * crandom(); velocity[2] = -50 + 100 * crandom(); le->leType = LE_FALL_SCALE_FADE; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); //le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time - (rand()&15); AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = -4; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); le->bounceFactor = 0.4 * waterScale; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; le = CG_SmokePuff( le->origin, le->velocity, 30, // radius 1, 1, 1, 1, // color 2000, // trailTime cg.time, // startTime 0, // fadeInTime 0, // flags cgs.media.lbumShader1 ); le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_NONE; le->leMarkType = LEMT_NONE; } */ /* ================= CG_MissileHitWall Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing ================= */ void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ) { qhandle_t mod; qhandle_t mark; qhandle_t shader; sfxHandle_t sfx; float radius; float light; vec3_t lightColor; localEntity_t *le; int r; qboolean alphaFade; qboolean isSprite; int duration; vec3_t sprOrg; vec3_t sprVel; mark = 0; radius = 32; sfx = 0; mod = 0; shader = 0; light = 0; lightColor[0] = 1; lightColor[1] = 1; lightColor[2] = 0; // set defaults isSprite = qfalse; duration = 600; switch ( weapon ) { default: //#ifdef MISSIONPACK case WP_NAILGUN: if( soundType == IMPACTSOUND_FLESH ) { sfx = cgs.media.sfx_nghitflesh; } else if( soundType == IMPACTSOUND_METAL ) { sfx = cgs.media.sfx_nghitmetal; } else { sfx = cgs.media.sfx_nghit; } mark = cgs.media.holeMarkShader; radius = 12; break; //#endif case WP_LIGHTNING: // no explosion at LG impact, it is added with the beam r = rand() & 3; if ( r < 2 ) { sfx = cgs.media.sfx_lghit2; } else if ( r == 2 ) { sfx = cgs.media.sfx_lghit1; } else { sfx = cgs.media.sfx_lghit3; } mark = cgs.media.holeMarkShader; radius = 12; break; //#ifdef MISSIONPACK case WP_PROX_LAUNCHER: mod = cgs.media.dishFlashModel; shader = cgs.media.grenadeExplosionShader; sfx = cgs.media.sfx_proxexp; mark = cgs.media.burnMarkShader; radius = 64; light = 300; isSprite = qtrue; // LEILEI START enhancement if (cg_leiEnhancement.integer) { // some more fireball, fireball, fireball, fire fire! VectorMA( origin, 24, dir, sprOrg ); VectorScale( dir, 64, sprVel ); lightColor[0] = 0.7; // subtler explosion colors lightColor[1] = 0.6; lightColor[2] = 0.4; VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 2, sprVel ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 42, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 700, 10, 108 ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 82, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 400, 11, 158 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 182, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 600, 47, 88 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 64, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 110, 72, 28 ); mod = 0; // turns off the sprite (unfortunately, disables dlight) } // LEILEI END enhancement break; //#endif case WP_GRENADE_LAUNCHER: mod = cgs.media.dishFlashModel; shader = cgs.media.grenadeExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 64; light = 300; isSprite = qtrue; // LEILEI START enhancement if (cg_leiEnhancement.integer) { // some more fireball, fireball, fireball, fire fire! VectorMA( origin, 24, dir, sprOrg ); VectorScale( dir, 64, sprVel ); lightColor[0] = 0.7; // subtler explosion colors lightColor[1] = 0.6; lightColor[2] = 0.4; VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 2, sprVel ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 42, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 700, 10, 108 ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 82, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 400, 11, 158 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 182, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 600, 47, 88 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 64, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 110, 72, 28 ); mod = 0; // turns off the sprite (unfortunately, disables dlight) } // LEILEI END enhancement break; case WP_ROCKET_LAUNCHER: mod = cgs.media.dishFlashModel; shader = cgs.media.rocketExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 64; light = 300; isSprite = qtrue; duration = 1000; lightColor[0] = 1; lightColor[1] = 0.75; lightColor[2] = 0.0; if (!cg_oldRocket.integer) { // explosion sprite animation VectorMA( origin, 24, dir, sprOrg ); VectorScale( dir, 64, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 20, 30 ); } // LEILEI START enhancement if (cg_leiEnhancement.integer) { // some more fireball, fireball, fireball, fire fire! VectorMA( origin, 24, dir, sprOrg ); VectorScale( dir, 64, sprVel ); lightColor[0] = 0.7; // subtler explosion colors lightColor[1] = 0.6; lightColor[2] = 0.4; VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 2, sprVel ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 42, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 700, 10, 108 ); VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 82, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 400, 11, 158 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 182, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 600, 47, 88 ); VectorMA( origin, -5, dir, sprOrg ); VectorScale( dir, 64, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 110, 72, 28 ); mod = 0; // turns off the sprite (unfortunately, disables dlight) } // LEILEI END enhancement break; case WP_RAILGUN: mod = cgs.media.ringFlashModel; shader = cgs.media.railExplosionShader; sfx = cgs.media.sfx_plasmaexp; mark = cgs.media.energyMarkShader; radius = 24; break; case WP_PLASMAGUN: mod = cgs.media.ringFlashModel; shader = cgs.media.plasmaExplosionShader; sfx = cgs.media.sfx_plasmaexp; mark = cgs.media.energyMarkShader; radius = 16; break; case WP_BFG: mod = cgs.media.dishFlashModel; shader = cgs.media.bfgExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 32; isSprite = qtrue; break; case WP_SHOTGUN: mod = cgs.media.bulletFlashModel; shader = cgs.media.bulletExplosionShader; mark = cgs.media.bulletMarkShader; #if 0 //Some problems here if (cg_leiEnhancement.integer) { if( soundType == IMPACTSOUND_FLESH ) { mark = cgs.media.lbldShader2; } else if( soundType == IMPACTSOUND_METAL ) { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkmetal1; } else if ( r == 2 ) { mark = cgs.media.lmarkmetal2; } else if ( r == 1 ) { mark = cgs.media.lmarkmetal3; } else { mark = cgs.media.lmarkmetal4; } } else { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkbullet1; } else if ( r == 2 ) { mark = cgs.media.lmarkbullet2; } else if ( r == 1 ) { mark = cgs.media.lmarkbullet3; } else { mark = cgs.media.lmarkbullet4; } } } #endif sfx = 0; radius = 4; break; //#ifdef MISSIONPACK case WP_CHAINGUN: mod = cgs.media.bulletFlashModel; if( soundType == IMPACTSOUND_FLESH ) { sfx = cgs.media.sfx_chghitflesh; } else if( soundType == IMPACTSOUND_METAL ) { sfx = cgs.media.sfx_chghitmetal; } else { sfx = cgs.media.sfx_chghit; } mark = cgs.media.bulletMarkShader; #if 0 //Some problems here if (cg_leiEnhancement.integer) { if( soundType == IMPACTSOUND_FLESH ) { mark = cgs.media.lbldShader2; } else if( soundType == IMPACTSOUND_METAL ) { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkmetal1; } else if ( r == 2 ) { mark = cgs.media.lmarkmetal2; } else if ( r == 1 ) { mark = cgs.media.lmarkmetal3; } else { mark = cgs.media.lmarkmetal4; } } else { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkbullet1; } else if ( r == 2 ) { mark = cgs.media.lmarkbullet2; } else if ( r == 1 ) { mark = cgs.media.lmarkbullet3; } else { mark = cgs.media.lmarkbullet4; } } } #endif r = rand() & 3; if ( r < 2 ) { sfx = cgs.media.sfx_ric1; } else if ( r == 2 ) { sfx = cgs.media.sfx_ric2; } else { sfx = cgs.media.sfx_ric3; } radius = 8; break; //#endif case WP_MACHINEGUN: mod = cgs.media.bulletFlashModel; shader = cgs.media.bulletExplosionShader; mark = cgs.media.bulletMarkShader; #if 0 //Some problems here if (cg_leiEnhancement.integer) { if( soundType == IMPACTSOUND_FLESH ) { mark = cgs.media.lbldShader2; } else if( soundType == IMPACTSOUND_METAL ) { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkmetal1; } else if ( r == 2 ) { mark = cgs.media.lmarkmetal2; } else if ( r == 1 ) { mark = cgs.media.lmarkmetal3; } else { mark = cgs.media.lmarkmetal4; } } else { r = rand() & 4; if ( r < 3 ) { mark = cgs.media.lmarkbullet1; } else if ( r == 2 ) { mark = cgs.media.lmarkbullet2; } else if ( r == 1 ) { mark = cgs.media.lmarkbullet3; } else { mark = cgs.media.lmarkbullet4; } VectorMA( origin, 4, dir, sprOrg ); VectorScale( dir, 82, sprVel ); } } #endif r = rand() & 3; if ( r == 0 ) { sfx = cgs.media.sfx_ric1; } else if ( r == 1 ) { sfx = cgs.media.sfx_ric2; } else { sfx = cgs.media.sfx_ric3; } radius = 8; break; } if ( sfx ) { trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx ); } // // create the explosion // if ( mod ) { le = CG_MakeExplosion( origin, dir, mod, shader, duration, isSprite ); le->light = light; VectorCopy( lightColor, le->lightColor ); if ( weapon == WP_RAILGUN ) { // colorize with client color VectorCopy( cgs.clientinfo[clientNum].color1, le->color ); le->refEntity.shaderRGBA[0] = le->color[0] * 0xff; le->refEntity.shaderRGBA[1] = le->color[1] * 0xff; le->refEntity.shaderRGBA[2] = le->color[2] * 0xff; le->refEntity.shaderRGBA[3] = 0xff; } } // // impact mark // alphaFade = (mark == cgs.media.energyMarkShader); // plasma fades alpha, all others fade color if ( weapon == WP_RAILGUN ) { float *color; // colorize with client color color = cgs.clientinfo[clientNum].color1; CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse ); } else { CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse ); } } /* ================= CG_MissileHitPlayer ================= */ void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) { // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { CG_SmokePuff( origin, dir, 22, 1, 1, 1, 1.0f, 900, cg.time, 0, 0, cgs.media.lbldShader1 ); CG_SpurtBlood( origin, dir, 1); // CG_SpurtBlood( origin, dir, 4); // CG_SpurtBlood( origin, dir, -12); } else CG_Bleed( origin, entityNum ); // some weapons will make an explosion with the blood, while // others will just make the blood switch ( weapon ) { case WP_GRENADE_LAUNCHER: case WP_ROCKET_LAUNCHER: //#ifdef MISSIONPACK case WP_NAILGUN: case WP_CHAINGUN: case WP_PROX_LAUNCHER: //#endif CG_MissileHitWall( weapon, 0, origin, dir, IMPACTSOUND_FLESH ); break; default: break; } } /* ============================================================================ SHOTGUN TRACING ============================================================================ */ /* ================ CG_ShotgunPellet ================ */ static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) { trace_t tr; int sourceContentType, destContentType; // LEILEI ENHACNEMENT localEntity_t *smoke; vec3_t kapow; CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT ); sourceContentType = CG_PointContents( start, 0 ); destContentType = CG_PointContents( tr.endpos, 0 ); // FIXME: should probably move this cruft into CG_BubbleTrail if ( sourceContentType == destContentType ) { if ( sourceContentType & CONTENTS_WATER ) { CG_BubbleTrail( start, tr.endpos, 32 ); } } else if ( sourceContentType & CONTENTS_WATER ) { trace_t trace; trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( start, trace.endpos, 32 ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Water Splash VectorCopy( trace.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 22); kapow[1] = kapow[1] * (crandom() * 22); kapow[2] = kapow[2] * (crandom() * 65 + 37); smoke = CG_SmokePuff( trace.endpos, kapow, 14, 1, 1, 1, 1.0f, 400, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 6, 1, 1, 1, 1.0f, 200, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 10, 1, 1, 1, 1.0f, 300, cg.time, 0, 0, cgs.media.lsplShader ); } // END LEIHANCMENET } else if ( destContentType & CONTENTS_WATER ) { trace_t trace; trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( tr.endpos, trace.endpos, 32 ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Water Splash VectorCopy( trace.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 22); kapow[1] = kapow[1] * (crandom() * 22); kapow[2] = kapow[2] * (crandom() * 65 + 37); smoke = CG_SmokePuff( trace.endpos, kapow, 14, 1, 1, 1, 1.0f, 400, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 6, 1, 1, 1, 1.0f, 200, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 10, 1, 1, 1, 1.0f, 300, cg.time, 0, 0, cgs.media.lsplShader ); } // END LEIHANCMENET } if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } if ( cg_entities[tr.entityNum].currentState.eType == ET_PLAYER ) { CG_MissileHitPlayer( WP_SHOTGUN, tr.endpos, tr.plane.normal, tr.entityNum ); } else { if ( tr.surfaceFlags & SURF_NOIMPACT ) { // SURF_NOIMPACT will not make a flame puff or a mark return; } if ( tr.surfaceFlags & SURF_METALSTEPS ) { CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { VectorCopy( tr.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 65 + 37); kapow[1] = kapow[1] * (crandom() * 65 + 37); kapow[2] = kapow[2] * (crandom() * 65 + 37); CG_LeiSparks(tr.endpos, tr.plane.normal, 800, 0, 0, 7); CG_LeiSparks(tr.endpos, tr.plane.normal, 800, 0, 0, 3); CG_LeiSparks(tr.endpos, tr.plane.normal, 800, 0, 0, 1); } // END LEIHANCMENET } else { CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { VectorCopy( tr.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 65 + 37); kapow[1] = kapow[1] * (crandom() * 65 + 37); kapow[2] = kapow[2] * (crandom() * 65 + 37); CG_LeiSparks(tr.endpos, tr.plane.normal, 800, 0, 0, 7); CG_LeiSparks(tr.endpos, tr.plane.normal, 800, 0, 0, 2); smoke = CG_SmokePuff( tr.endpos, kapow, 21, 1, 1, 1, 0.9f, 1200, cg.time, 0, 0, cgs.media.lsmkShader2 ); //smoke = CG_SmokePuff( tr.endpos, kapow, 21, 1, 1, 1, 0.9f, 1200, cg.time, 0, 0, cgs.media.lbumShader1 ); #if 0 CG_LeiPuff(tr.endpos, kapow, 500, 0, 0, 177, 6); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 127, 12); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 77, 16); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 127, 12); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 77, 16); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 127, 12); CG_LeiPuff(tr.endpos, tr.plane.normal, 500, 0, 0, 77, 16); #endif } // END LEIHANCMENET } } } /* ================ CG_ShotgunPattern Perform the same traces the server did to locate the hit splashes ================ */ //unlagged - attack prediction // made this non-static for access from cg_unlagged.c void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum ) { int i; float r, u; vec3_t end; vec3_t forward, right, up; // derive the right and up vectors from the forward vector, because // the client won't have any other information VectorNormalize2( origin2, forward ); PerpendicularVector( right, forward ); CrossProduct( forward, right, up ); // generate the "random" spread pattern for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) { r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; VectorMA( origin, 8192 * 16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); CG_ShotgunPellet( origin, end, otherEntNum ); } } /* ============== CG_ShotgunFire ============== */ void CG_ShotgunFire( entityState_t *es ) { vec3_t v; int contents; VectorSubtract( es->origin2, es->pos.trBase, v ); VectorNormalize( v ); VectorScale( v, 32, v ); VectorAdd( es->pos.trBase, v, v ); if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) { // ragepro can't alpha fade, so don't even bother with smoke vec3_t up; vec3_t forward; contents = CG_PointContents( es->pos.trBase, 0 ); if ( !( contents & CONTENTS_WATER ) ) { VectorSet( up, 0, 0, 8 ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Shotgun puffy CG_LeiSparks(v, forward, 1500, 0, 0, 7); CG_LeiSparks(v, forward, 1500, 0, 0, 7); CG_LeiSparks(v, forward, 1500, 0, 0, 7); CG_LeiSparks(v, forward, 1500, 0, 0, 7); CG_LeiSparks(v, forward, 1500, 0, 0, 7); CG_LeiSparks(v, forward, 1500, 0, 0, 7); /* VectorSet( up, 4, 4, 4 ); up[0] = up[0] * (crandom() * 22 + 44); up[1] = up[1] * (crandom() * 22 + 44); up[2] = up[2] * (crandom() * 22 + 44); CG_SmokePuff( v, up, 14, 1, 1, 1, 0.4f, 900, cg.time, 0, 0, cgs.media.lsmkShader1 ); up[0] = up[0] * (crandom() * 22 + 44); up[1] = up[1] * (crandom() * 22 + 44); up[2] = up[2] * (crandom() * 22 + 44); CG_SmokePuff( v, up, 14, 1, 1, 1, 0.4f, 900, cg.time, 0, 0, cgs.media.lsmkShader2 ); up[0] = up[0] * (crandom() * 22 + 44); up[1] = up[1] * (crandom() * 22 + 44); up[2] = up[2] * (crandom() * 22 + 44); CG_SmokePuff( v, up, 14, 1, 1, 1, 0.4f, 900, cg.time, 0, 0, cgs.media.lsmkShader3 ); up[0] = up[0] * (crandom() * 22 + 44); up[1] = up[1] * (crandom() * 22 + 44); up[2] = up[2] * (crandom() * 22 + 44); CG_SmokePuff( v, up, 14, 1, 1, 1, 0.4f, 900, cg.time, 0, 0, cgs.media.lsmkShader4 ); */ } else // END LEIHANCMENET CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33f, 900, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); } } CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum ); } /* ============================================================================ BULLETS ============================================================================ */ /* =============== CG_Tracer =============== */ void CG_Tracer( vec3_t source, vec3_t dest ) { vec3_t forward, right; polyVert_t verts[4]; vec3_t line; float len, begin, end; vec3_t start, finish; vec3_t midpoint; // tracer VectorSubtract( dest, source, forward ); len = VectorNormalize( forward ); // start at least a little ways from the muzzle if ( len < 100 ) { return; } begin = 50 + random() * (len - 60); end = begin + cg_tracerLength.value; if ( end > len ) { end = len; } VectorMA( source, begin, forward, start ); VectorMA( source, end, forward, finish ); line[0] = DotProduct( forward, cg.refdef.viewaxis[1] ); line[1] = DotProduct( forward, cg.refdef.viewaxis[2] ); VectorScale( cg.refdef.viewaxis[1], line[1], right ); VectorMA( right, -line[0], cg.refdef.viewaxis[2], right ); VectorNormalize( right ); VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz ); verts[0].st[0] = 0; verts[0].st[1] = 1; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz ); verts[1].st[0] = 1; verts[1].st[1] = 0; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz ); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz ); verts[3].st[0] = 0; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts ); midpoint[0] = ( start[0] + finish[0] ) * 0.5; midpoint[1] = ( start[1] + finish[1] ) * 0.5; midpoint[2] = ( start[2] + finish[2] ) * 0.5; // add the tracer sound trap_S_StartSound( midpoint, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.tracerSound ); } /* ====================== CG_CalcMuzzlePoint ====================== */ static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) { vec3_t forward; centity_t *cent; int anim; if ( entityNum == cg.snap->ps.clientNum ) { VectorCopy( cg.snap->ps.origin, muzzle ); muzzle[2] += cg.snap->ps.viewheight; AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); VectorMA( muzzle, 14, forward, muzzle ); return qtrue; } cent = &cg_entities[entityNum]; if ( !cent->currentValid ) { return qfalse; } VectorCopy( cent->currentState.pos.trBase, muzzle ); AngleVectors( cent->currentState.apos.trBase, forward, NULL, NULL ); anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if ( anim == LEGS_WALKCR || anim == LEGS_IDLECR ) { muzzle[2] += CROUCH_VIEWHEIGHT; } else { muzzle[2] += DEFAULT_VIEWHEIGHT; } VectorMA( muzzle, 14, forward, muzzle ); return qtrue; } /* ====================== CG_Bullet Renders bullet effects. ====================== */ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) { trace_t trace; int sourceContentType, destContentType; vec3_t start; // LEILEI ENHACNEMENT localEntity_t *smoke; vec3_t kapew; vec3_t kapow; // if the shooter is currently valid, calc a source point and possibly // do trail effects if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) { if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) { sourceContentType = CG_PointContents( start, 0 ); destContentType = CG_PointContents( end, 0 ); // do a complete bubble trail if necessary if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) { CG_BubbleTrail( start, end, 32 ); } // bubble trail from water into air else if ( ( sourceContentType & CONTENTS_WATER ) ) { trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( start, trace.endpos, 32 ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Water Splash VectorCopy( trace.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 22); kapow[1] = kapow[1] * (crandom() * 22); kapow[2] = kapow[2] * (crandom() * 65 + 37); smoke = CG_SmokePuff( trace.endpos, kapow, 14, 1, 1, 1, 1.0f, 400, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 6, 1, 1, 1, 1.0f, 200, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 10, 1, 1, 1, 1.0f, 300, cg.time, 0, 0, cgs.media.lsplShader ); // CG_LeiSplash2(trace.endpos, kapow, 900, 0, 0, 444); } // END LEIHANCMENET } // bubble trail from air into water else if ( ( destContentType & CONTENTS_WATER ) ) { trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( trace.endpos, end, 32 ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Water Splash VectorCopy( trace.plane.normal, kapow ); kapow[0] = kapow[0] * (crandom() * 22); kapow[1] = kapow[1] * (crandom() * 22); kapow[2] = kapow[2] * (crandom() * 65 + 37); smoke = CG_SmokePuff( trace.endpos, kapow, 14, 1, 1, 1, 1.0f, 400, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 6, 1, 1, 1, 1.0f, 200, cg.time, 0, 0, cgs.media.lsplShader ); smoke = CG_SmokePuff( trace.endpos, kapow, 10, 1, 1, 1, 1.0f, 300, cg.time, 0, 0, cgs.media.lsplShader ); //CG_LeiSplash2(trace.endpos, kapow, 500, 0, 0, 1); } // END LEIHANCMENET } // draw a tracer if ( random() < cg_tracerChance.value ) { CG_Tracer( start, end ); } } } // impact splash and mark if ( flesh ) { // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Blood Hack VectorCopy( normal, kapow ); kapow[0] = kapow[0] * (crandom() * 65 + 37); kapow[1] = kapow[1] * (crandom() * 65 + 37); kapow[2] = kapow[2] * (crandom() * 65 + 37); VectorCopy( kapow, kapew ); kapew[0] = kapew[0] * (crandom() * 2 + 37); kapew[1] = kapew[1] * (crandom() * 2 + 37); kapew[2] = kapew[2] * (crandom() * 2 + 37); CG_SmokePuff( end, kapow, 6, 1, 1, 1, 1.0f, 600, cg.time, 0, 0, cgs.media.lbldShader1 ); // CG_SpurtBlood( end, kapow, 2); CG_SpurtBlood( end, kapew, 1); //CG_Particle_Bleed(cgs.media.lbldShader1,kapew,'0 0 0', 0, 100); // CG_Particle_Bleed(cgs.media.lbldShader1,kapew,kapow, 0, 100); // CG_Particle_BloodCloud(self,end,'0 0 0'); if (cg_leiSuperGoreyAwesome.integer) { CG_SpurtBlood( end, kapow, -2); } } else CG_Bleed( end, fleshEntityNum ); } else { CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT ); // LEILEI ENHANCEMENT if (cg_leiEnhancement.integer) { // Smoke puff VectorCopy( normal, kapow ); kapow[0] = kapow[0] * (crandom() * 65 + 37); kapow[1] = kapow[1] * (crandom() * 65 + 37); kapow[2] = kapow[2] * (crandom() * 65 + 37); VectorCopy( kapow, kapew ); kapew[0] = kapew[0] * (crandom() * 65 + 37); kapew[1] = kapew[1] * (crandom() * 65 + 37); kapew[2] = kapew[2] * (crandom() * 65 + 37); smoke = CG_SmokePuff( end, kapow, 14, 1, 1, 1, 1.0f, 600, cg.time, 0, 0, cgs.media.lsmkShader1 ); // CG_LeiSparks(end, normal, 600, 0, 0, 177); // CG_LeiSparks(end, normal, 600, 0, 0, 155); // CG_LeiSparks(end, normal, 600, 0, 0, 444); // CG_LeiSparks(trace.endpos, trace.plane.normal, 800, 0, 0, 7); // CG_LeiSparks(trace.endpos, trace.plane.normal, 800, 0, 0, 3); // CG_LeiSparks(trace.endpos, trace.plane.normal, 800, 0, 0, 1); } // END LEIHANCMENET } } openarena_0.8.8.orig/code/tools/0000755000175000017500000000000011720470203015270 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/asm/0000755000175000017500000000000011720470205016052 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/asm/cmdlib.c0000644000175000017500000004741211656310262017464 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cmdlib.c #include "cmdlib.h" #include #include #ifdef WIN32 #include #include #elif defined(NeXT) #include #else #include #endif #define BASEDIRNAME "quake" // assumed to have a 2 or 3 following #define PATHSEPERATOR '/' // set these before calling CheckParm int myargc; char **myargv; char com_token[1024]; qboolean com_eof; qboolean archive; char archivedir[1024]; /* =================== ExpandWildcards Mimic unix command line expansion =================== */ #define MAX_EX_ARGC 1024 int ex_argc; char *ex_argv[MAX_EX_ARGC]; #ifdef _WIN32 #include "io.h" void ExpandWildcards( int *argc, char ***argv ) { struct _finddata_t fileinfo; int handle; int i; char filename[1024]; char filebase[1024]; char *path; ex_argc = 0; for (i=0 ; i<*argc ; i++) { path = (*argv)[i]; if ( path[0] == '-' || ( !strstr(path, "*") && !strstr(path, "?") ) ) { ex_argv[ex_argc++] = path; continue; } handle = _findfirst (path, &fileinfo); if (handle == -1) return; ExtractFilePath (path, filebase); do { sprintf (filename, "%s%s", filebase, fileinfo.name); ex_argv[ex_argc++] = copystring (filename); } while (_findnext( handle, &fileinfo ) != -1); _findclose (handle); } *argc = ex_argc; *argv = ex_argv; } #else void ExpandWildcards (int *argc, char ***argv) { } #endif #ifdef WIN_ERROR #include /* ================= Error For abnormal program terminations in windowed apps ================= */ void Error( const char *error, ... ) { va_list argptr; char text[1024]; char text2[1024]; int err; err = GetLastError (); va_start (argptr,error); vsprintf (text, error,argptr); va_end (argptr); sprintf (text2, "%s\nGetLastError() = %i", text, err); MessageBox(NULL, text2, "Error", 0 /* MB_OK */ ); exit (1); } #else /* ================= Error For abnormal program terminations in console apps ================= */ void Error( const char *error, ...) { va_list argptr; _printf ("\n************ ERROR ************\n"); va_start (argptr,error); vprintf (error,argptr); va_end (argptr); _printf ("\r\n"); exit (1); } #endif // only printf if in verbose mode qboolean verbose = qfalse; void qprintf( const char *format, ... ) { va_list argptr; if (!verbose) return; va_start (argptr,format); vprintf (format,argptr); va_end (argptr); } #ifdef WIN32 HWND hwndOut = NULL; qboolean lookedForServer = qfalse; UINT wm_BroadcastCommand = -1; #endif void _printf( const char *format, ... ) { va_list argptr; char text[4096]; #ifdef WIN32 ATOM a; #endif va_start (argptr,format); vsprintf (text, format, argptr); va_end (argptr); printf(text); #ifdef WIN32 if (!lookedForServer) { lookedForServer = qtrue; hwndOut = FindWindow(NULL, "Q3Map Process Server"); if (hwndOut) { wm_BroadcastCommand = RegisterWindowMessage( "Q3MPS_BroadcastCommand" ); } } if (hwndOut) { a = GlobalAddAtom(text); PostMessage(hwndOut, wm_BroadcastCommand, 0, (LPARAM)a); } #endif } /* qdir will hold the path up to the quake directory, including the slash f:\quake\ /raid/quake/ gamedir will hold qdir + the game directory (id1, id2, etc) */ char qdir[1024]; char gamedir[1024]; char writedir[1024]; void SetQdirFromPath( const char *path ) { char temp[1024]; const char *c; const char *sep; int len, count; if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) { // path is partial Q_getwd (temp); strcat (temp, path); path = temp; } // search for "quake2" in path len = strlen(BASEDIRNAME); for (c=path+strlen(path)-1 ; c != path ; c--) { int i; if (!Q_strncasecmp (c, BASEDIRNAME, len)) { // //strncpy (qdir, path, c+len+2-path); // the +2 assumes a 2 or 3 following quake which is not the // case with a retail install // so we need to add up how much to the next separator sep = c + len; count = 1; while (*sep && *sep != '/' && *sep != '\\') { sep++; count++; } strncpy (qdir, path, c+len+count-path); qprintf ("qdir: %s\n", qdir); for ( i = 0; i < strlen( qdir ); i++ ) { if ( qdir[i] == '\\' ) qdir[i] = '/'; } c += len+count; while (*c) { if (*c == '/' || *c == '\\') { strncpy (gamedir, path, c+1-path); for ( i = 0; i < strlen( gamedir ); i++ ) { if ( gamedir[i] == '\\' ) gamedir[i] = '/'; } qprintf ("gamedir: %s\n", gamedir); if ( !writedir[0] ) strcpy( writedir, gamedir ); else if ( writedir[strlen( writedir )-1] != '/' ) { writedir[strlen( writedir )] = '/'; writedir[strlen( writedir )+1] = 0; } return; } c++; } Error ("No gamedir in %s", path); return; } } Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); } char *ExpandArg (const char *path) { static char full[1024]; if (path[0] != '/' && path[0] != '\\' && path[1] != ':') { Q_getwd (full); strcat (full, path); } else strcpy (full, path); return full; } char *ExpandPath (const char *path) { static char full[1024]; if (!qdir[0]) Error ("ExpandPath called without qdir set"); if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { strcpy( full, path ); return full; } sprintf (full, "%s%s", qdir, path); return full; } char *ExpandGamePath (const char *path) { static char full[1024]; if (!qdir[0]) Error ("ExpandGamePath called without qdir set"); if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { strcpy( full, path ); return full; } sprintf (full, "%s%s", gamedir, path); return full; } char *ExpandPathAndArchive (const char *path) { char *expanded; char archivename[1024]; expanded = ExpandPath (path); if (archive) { sprintf (archivename, "%s/%s", archivedir, path); QCopyFile (expanded, archivename); } return expanded; } char *copystring(const char *s) { char *b; b = malloc(strlen(s)+1); strcpy (b, s); return b; } /* ================ I_FloatTime ================ */ double I_FloatTime (void) { time_t t; time (&t); return t; #if 0 // more precise, less portable struct timeval tp; struct timezone tzp; static int secbase; gettimeofday(&tp, &tzp); if (!secbase) { secbase = tp.tv_sec; return tp.tv_usec/1000000.0; } return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; #endif } void Q_getwd (char *out) { int i = 0; #ifdef WIN32 _getcwd (out, 256); strcat (out, "\\"); #else getcwd (out, 256); strcat (out, "/"); #endif while ( out[i] != 0 ) { if ( out[i] == '\\' ) out[i] = '/'; i++; } } void Q_mkdir (const char *path) { #ifdef WIN32 if (_mkdir (path) != -1) return; #else if (mkdir (path, 0777) != -1) return; #endif if (errno != EEXIST) Error ("mkdir %s: %s",path, strerror(errno)); } /* ============ FileTime returns -1 if not present ============ */ int FileTime (const char *path) { struct stat buf; if (stat (path,&buf) == -1) return -1; return buf.st_mtime; } /* ============== COM_Parse Parse a token out of a string ============== */ char *COM_Parse (char *data) { int c; int len; len = 0; com_token[0] = 0; if (!data) return NULL; // skip whitespace skipwhite: while ( (c = *data) <= ' ') { if (c == 0) { com_eof = qtrue; return NULL; // end of file; } data++; } // skip // comments if (c=='/' && data[1] == '/') { while (*data && *data != '\n') data++; goto skipwhite; } // handle quoted strings specially if (c == '\"') { data++; do { c = *data++; if (c=='\"') { com_token[len] = 0; return data; } com_token[len] = c; len++; } while (1); } // parse single characters if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') { com_token[len] = c; len++; com_token[len] = 0; return data+1; } // parse a regular word do { com_token[len] = c; data++; len++; c = *data; if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') break; } while (c>32); com_token[len] = 0; return data; } int Q_strncasecmp (const char *s1, const char *s2, int n) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (!n--) return 0; // strings are equal until end point if (c1 != c2) { if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A'); if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A'); if (c1 != c2) return -1; // strings not equal } } while (c1); return 0; // strings are equal } int Q_stricmp (const char *s1, const char *s2) { return Q_strncasecmp (s1, s2, 99999); } char *strupr (char *start) { char *in; in = start; while (*in) { *in = toupper(*in); in++; } return start; } char *strlower (char *start) { char *in; in = start; while (*in) { *in = tolower(*in); in++; } return start; } /* ============================================================================= MISC FUNCTIONS ============================================================================= */ /* ================= CheckParm Checks for the given parameter in the program's command line arguments Returns the argument number (1 to argc-1) or 0 if not present ================= */ int CheckParm (const char *check) { int i; for (i = 1;i 0) { nAllocSize += MEM_BLOCKSIZE - nBlock; } buffer = malloc (nAllocSize+1); memset(buffer, 0, nAllocSize+1); SafeRead (f, buffer, length); fclose (f); *bufferptr = buffer; return length; } /* ============== TryLoadFile Allows failure ============== */ int TryLoadFile (const char *filename, void **bufferptr) { FILE *f; int length; void *buffer; *bufferptr = NULL; f = myfopen (filename, "rb"); if (!f) return -1; length = Q_filelength (f); buffer = malloc (length+1); ((char *)buffer)[length] = 0; SafeRead (f, buffer, length); fclose (f); *bufferptr = buffer; return length; } /* ============== SaveFile ============== */ void SaveFile (const char *filename, const void *buffer, int count) { FILE *f; f = SafeOpenWrite (filename); SafeWrite (f, buffer, count); fclose (f); } void DefaultExtension (char *path, const char *extension) { char *src; // // if path doesnt have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; while (*src != '/' && *src != '\\' && src != path) { if (*src == '.') return; // it has an extension src--; } strcat (path, extension); } void DefaultPath (char *path, const char *basepath) { char temp[128]; if (path[0] == PATHSEPERATOR) return; // absolute path location strcpy (temp,path); strcpy (path,basepath); strcat (path,temp); } void StripFilename (char *path) { int length; length = strlen(path)-1; while (length > 0 && path[length] != PATHSEPERATOR) length--; path[length] = 0; } void StripExtension (char *path) { int length; length = strlen(path)-1; while (length > 0 && path[length] != '.') { length--; if (path[length] == '/') return; // no extension } if (length) path[length] = 0; } /* ==================== Extract file parts ==================== */ // FIXME: should include the slash, otherwise // backing to an empty path will be wrong when appending a slash void ExtractFilePath (const char *path, char *dest) { const char *src; src = path + strlen(path) - 1; // // back up until a \ or the start // while (src != path && *(src-1) != '\\' && *(src-1) != '/') src--; memcpy (dest, path, src-path); dest[src-path] = 0; } void ExtractFileBase (const char *path, char *dest) { const char *src; src = path + strlen(path) - 1; // // back up until a \ or the start // while (src != path && *(src-1) != PATHSEPERATOR) src--; while (*src && *src != '.') { *dest++ = *src++; } *dest = 0; } void ExtractFileExtension (const char *path, char *dest) { const char *src; src = path + strlen(path) - 1; // // back up until a . or the start // while (src != path && *(src-1) != '.') src--; if (src == path) { *dest = 0; // no extension return; } strcpy (dest,src); } /* ============== ParseNum / ParseHex ============== */ int ParseHex (const char *hex) { const char *str; int num; num = 0; str = hex; while (*str) { num <<= 4; if (*str >= '0' && *str <= '9') num += *str-'0'; else if (*str >= 'a' && *str <= 'f') num += 10 + *str-'a'; else if (*str >= 'A' && *str <= 'F') num += 10 + *str-'A'; else Error ("Bad hex number: %s",hex); str++; } return num; } int ParseNum (const char *str) { if (str[0] == '$') return ParseHex (str+1); if (str[0] == '0' && str[1] == 'x') return ParseHex (str+2); return atol (str); } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ short ShortSwap (short l) { byte b1,b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } int LongSwap (int l) { byte b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } typedef union { float f; unsigned int i; } _FloatByteUnion; float FloatSwap (const float *f) { _FloatByteUnion out; out.f = *f; out.i = LongSwap(out.i); return out.f; } //======================================================= // FIXME: byte swap? // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the // CCITT standard CRC used by XMODEM #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 static unsigned short 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 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } void CRC_ProcessByte(unsigned short *crcvalue, byte data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } //============================================================================= /* ============ CreatePath ============ */ void CreatePath (const char *path) { const char *ofs; char c; char dir[1024]; #ifdef _WIN32 int olddrive = -1; if ( path[1] == ':' ) { olddrive = _getdrive(); _chdrive( toupper( path[0] ) - 'A' + 1 ); } #endif if (path[1] == ':') path += 2; for (ofs = path+1 ; *ofs ; ofs++) { c = *ofs; if (c == '/' || c == '\\') { // create the directory memcpy( dir, path, ofs - path ); dir[ ofs - path ] = 0; Q_mkdir( dir ); } } #ifdef _WIN32 if ( olddrive != -1 ) { _chdrive( olddrive ); } #endif } /* ============ QCopyFile Used to archive source files ============ */ void QCopyFile (const char *from, const char *to) { void *buffer; int length; length = LoadFile (from, &buffer); CreatePath (to); SaveFile (to, buffer, length); free (buffer); } openarena_0.8.8.orig/code/tools/asm/README.Id0000644000175000017500000000063611656310262017276 0ustar smcvsmcv2002-10-25 Timothee Besset If you are looking for a faster version of the q3asm tool, try: http://www.icculus.org/~phaethon/q3/q3asm-turbo/q3asm-turbo.html 2001-10-31 Timothee Besset updated from the $/source/q3asm code modified for portability and use with >= 1.31 mod source release the cmdlib.c cmdlib.h mathlib.h qfiles.h have been copied from $/source/common openarena_0.8.8.orig/code/tools/asm/opstrings.h0000644000175000017500000000711011656310262020256 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ { "BREAK", OP_BREAK }, { "CNSTF4", OP_CONST }, { "CNSTI4", OP_CONST }, { "CNSTP4", OP_CONST }, { "CNSTU4", OP_CONST }, { "CNSTI2", OP_CONST }, { "CNSTU2", OP_CONST }, { "CNSTI1", OP_CONST }, { "CNSTU1", OP_CONST }, //{ "ARGB", OP_ARG }, //{ "ARGF", OP_ARG }, //{ "ARGI", OP_ARG }, //{ "ARGP", OP_ARG }, //{ "ARGU", OP_ARG }, { "ASGNB", OP_BLOCK_COPY }, { "ASGNF4", OP_STORE4 }, { "ASGNI4", OP_STORE4 }, { "ASGNP4", OP_STORE4 }, { "ASGNU4", OP_STORE4 }, { "ASGNI2", OP_STORE2 }, { "ASGNU2", OP_STORE2 }, { "ASGNI1", OP_STORE1 }, { "ASGNU1", OP_STORE1 }, { "INDIRB", OP_IGNORE }, // block copy deals with this { "INDIRF4", OP_LOAD4 }, { "INDIRI4", OP_LOAD4 }, { "INDIRP4", OP_LOAD4 }, { "INDIRU4", OP_LOAD4 }, { "INDIRI2", OP_LOAD2 }, { "INDIRU2", OP_LOAD2 }, { "INDIRI1", OP_LOAD1 }, { "INDIRU1", OP_LOAD1 }, { "CVFF4", OP_UNDEF }, { "CVFI4", OP_CVFI }, { "CVIF4", OP_CVIF }, { "CVII4", OP_SEX8 }, // will be either SEX8 or SEX16 { "CVII1", OP_IGNORE }, { "CVII2", OP_IGNORE }, { "CVIU4", OP_IGNORE }, { "CVPU4", OP_IGNORE }, { "CVUI4", OP_IGNORE }, { "CVUP4", OP_IGNORE }, { "CVUU4", OP_IGNORE }, { "CVUU1", OP_IGNORE }, { "NEGF4", OP_NEGF }, { "NEGI4", OP_NEGI }, //{ "CALLB", OP_UNDEF }, //{ "CALLF", OP_UNDEF }, //{ "CALLI", OP_UNDEF }, //{ "CALLP", OP_UNDEF }, //{ "CALLU", OP_UNDEF }, //{ "CALLV", OP_CALL }, //{ "RETF", OP_UNDEF }, //{ "RETI", OP_UNDEF }, //{ "RETP", OP_UNDEF }, //{ "RETU", OP_UNDEF }, //{ "RETV", OP_UNDEF }, { "ADDRGP4", OP_CONST }, //{ "ADDRFP", OP_PARM }, //{ "ADDRLP", OP_LOCAL }, { "ADDF4", OP_ADDF }, { "ADDI4", OP_ADD }, { "ADDP4", OP_ADD }, { "ADDP", OP_ADD }, { "ADDU4", OP_ADD }, { "SUBF4", OP_SUBF }, { "SUBI4", OP_SUB }, { "SUBP4", OP_SUB }, { "SUBU4", OP_SUB }, { "LSHI4", OP_LSH }, { "LSHU4", OP_LSH }, { "MODI4", OP_MODI }, { "MODU4", OP_MODU }, { "RSHI4", OP_RSHI }, { "RSHU4", OP_RSHU }, { "BANDI4", OP_BAND }, { "BANDU4", OP_BAND }, { "BCOMI4", OP_BCOM }, { "BCOMU4", OP_BCOM }, { "BORI4", OP_BOR }, { "BORU4", OP_BOR }, { "BXORI4", OP_BXOR }, { "BXORU4", OP_BXOR }, { "DIVF4", OP_DIVF }, { "DIVI4", OP_DIVI }, { "DIVU4", OP_DIVU }, { "MULF4", OP_MULF }, { "MULI4", OP_MULI }, { "MULU4", OP_MULU }, { "EQF4", OP_EQF }, { "EQI4", OP_EQ }, { "EQU4", OP_EQ }, { "GEF4", OP_GEF }, { "GEI4", OP_GEI }, { "GEU4", OP_GEU }, { "GTF4", OP_GTF }, { "GTI4", OP_GTI }, { "GTU4", OP_GTU }, { "LEF4", OP_LEF }, { "LEI4", OP_LEI }, { "LEU4", OP_LEU }, { "LTF4", OP_LTF }, { "LTI4", OP_LTI }, { "LTU4", OP_LTU }, { "NEF4", OP_NEF }, { "NEI4", OP_NE }, { "NEU4", OP_NE }, { "JUMPV", OP_JUMP }, { "LOADB4", OP_UNDEF }, { "LOADF4", OP_UNDEF }, { "LOADI4", OP_UNDEF }, { "LOADP4", OP_UNDEF }, { "LOADU4", OP_UNDEF }, openarena_0.8.8.orig/code/tools/asm/ops.txt0000644000175000017500000000120411656310262017415 0ustar smcvsmcvCNSTF, CNSTI, CNSTP, CNSTU, ARGB, ARGF, ARGI, ARGP, ARGU, ASGNB, ASGNF, ASGNI, ASGNP, ASGNU, INDIRB, INDIRF, INDIRI, INDIRP, INDIRU, CVFF, CVFI, CVIF, CVII, CVIU, CVPU, CVUI, CVUP, CVUU, NEGF, NEGI, CALLB, CALLF, CALLI, CALLP, CALLU, CALLV, RETF, RETI, RETP, RETU, RETV, ADDRGP, ADDRFP, ADDRLP, ADDF, ADDI, ADDP, ADDU, SUBF, SUBI, SUBP, SUBU, LSHI, LSHU, MODI, MODU, RSHI, RSHU, BANDI, BANDU, BCOMI, BCOMU, BORI, BORU, BXORI, BXORU, DIVF, DIVI, DIVU, MULF, MULI, MULU, EQF, EQI, EQU, GEF, GEI, GEU, GTF, GTI, GTU, LEF, LEI, LEU, LTF, LTI, LTU, NEF, NEI, NEU, JUMPV, LABELV, LOADB, LOADF, LOADI, LOADP, LOADU, openarena_0.8.8.orig/code/tools/asm/mathlib.h0000644000175000017500000000601711656310262017653 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef __MATHLIB__ #define __MATHLIB__ // mathlib.h #include #ifdef DOUBLEVEC_T typedef double vec_t; #else typedef float vec_t; #endif typedef vec_t vec2_t[3]; typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; #define SIDE_FRONT 0 #define SIDE_ON 2 #define SIDE_BACK 1 #define SIDE_CROSS -2 #define Q_PI 3.14159265358979323846 #define DEG2RAD( a ) ( ( (a) * Q_PI ) / 180.0F ) #define RAD2DEG( a ) ( ( (a) * 180.0f ) / Q_PI ) extern vec3_t vec3_origin; #define EQUAL_EPSILON 0.001 // plane types are used to speed some tests // 0-2 are axial planes #define PLANE_X 0 #define PLANE_Y 1 #define PLANE_Z 2 #define PLANE_NON_AXIAL 3 qboolean VectorCompare( const vec3_t v1, const vec3_t v2 ); #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 VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];} #define VectorClear(x) {x[0] = x[1] = x[2] = 0;} #define VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];} void Vec10Copy( vec_t *in, vec_t *out ); vec_t Q_rint (vec_t in); vec_t _DotProduct (vec3_t v1, vec3_t v2); void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); void _VectorCopy (vec3_t in, vec3_t out); void _VectorScale (vec3_t v, vec_t scale, vec3_t out); double VectorLength( const vec3_t v ); void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc ); void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); vec_t VectorNormalize( const vec3_t in, vec3_t out ); vec_t ColorNormalize( const vec3_t in, vec3_t out ); void VectorInverse (vec3_t v); void ClearBounds (vec3_t mins, vec3_t maxs); void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); void NormalToLatLong( const vec3_t normal, byte bytes[2] ); int PlaneTypeForNormal (vec3_t normal); #endif openarena_0.8.8.orig/code/tools/asm/notes.txt0000644000175000017500000000026611656310262017753 0ustar smcvsmcv don't do any paramter conversion (double to float, etc) Why? Security. Portability. It may be more aproachable. can still use regular dlls for development purposes lcc q3asm openarena_0.8.8.orig/code/tools/asm/q3asm.c0000644000175000017500000010320011656310262017242 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../qcommon/q_platform.h" #include "cmdlib.h" #include "mathlib.h" #include "../../qcommon/qfiles.h" /* 19079 total symbols in FI, 2002 Jan 23 */ #define DEFAULT_HASHTABLE_SIZE 2048 char outputFilename[MAX_OS_PATH]; // the zero page size is just used for detecting run time faults #define ZERO_PAGE_SIZE 0 // 256 typedef enum { OP_UNDEF, OP_IGNORE, OP_BREAK, OP_ENTER, OP_LEAVE, OP_CALL, OP_PUSH, OP_POP, OP_CONST, OP_LOCAL, OP_JUMP, //------------------- OP_EQ, OP_NE, OP_LTI, OP_LEI, OP_GTI, OP_GEI, OP_LTU, OP_LEU, OP_GTU, OP_GEU, OP_EQF, OP_NEF, OP_LTF, OP_LEF, OP_GTF, OP_GEF, //------------------- OP_LOAD1, OP_LOAD2, OP_LOAD4, OP_STORE1, OP_STORE2, OP_STORE4, // *(stack[top-1]) = stack[yop OP_ARG, OP_BLOCK_COPY, //------------------- OP_SEX8, OP_SEX16, OP_NEGI, OP_ADD, OP_SUB, OP_DIVI, OP_DIVU, OP_MODI, OP_MODU, OP_MULI, OP_MULU, OP_BAND, OP_BOR, OP_BXOR, OP_BCOM, OP_LSH, OP_RSHI, OP_RSHU, OP_NEGF, OP_ADDF, OP_SUBF, OP_DIVF, OP_MULF, OP_CVIF, OP_CVFI } opcode_t; typedef struct { int imageBytes; // after decompression int entryPoint; int stackBase; int stackSize; } executableHeader_t; typedef enum { CODESEG, DATASEG, // initialized 32 bit data, will be byte swapped LITSEG, // strings BSSSEG, // 0 filled JTRGSEG, // pseudo-segment that contains only jump table targets NUM_SEGMENTS } segmentName_t; #define MAX_IMAGE 0x400000 typedef struct { byte image[MAX_IMAGE]; int imageUsed; int segmentBase; // only valid on second pass } segment_t; typedef struct symbol_s { struct symbol_s *next; int hash; segment_t *segment; char *name; int value; } symbol_t; typedef struct hashchain_s { void *data; struct hashchain_s *next; } hashchain_t; typedef struct hashtable_s { int buckets; hashchain_t **table; } hashtable_t; int symtablelen = DEFAULT_HASHTABLE_SIZE; hashtable_t *symtable; hashtable_t *optable; segment_t segment[NUM_SEGMENTS]; segment_t *currentSegment; int passNumber; int numSymbols; int errorCount; typedef struct options_s { qboolean verbose; qboolean writeMapFile; qboolean vanillaQ3Compatibility; } options_t; options_t options = { 0 }; symbol_t *symbols; symbol_t *lastSymbol = 0; /* Most recent symbol defined. */ #define MAX_ASM_FILES 256 int numAsmFiles; char *asmFiles[MAX_ASM_FILES]; char *asmFileNames[MAX_ASM_FILES]; int currentFileIndex; char *currentFileName; int currentFileLine; //int stackSize = 16384; int stackSize = 0x10000; // we need to convert arg and ret instructions to // stores to the local stack frame, so we need to track the // characteristics of the current functions stack frame int currentLocals; // bytes of locals needed by this function int currentArgs; // bytes of largest argument list called from this function int currentArgOffset; // byte offset in currentArgs to store next arg, reset each call #define MAX_LINE_LENGTH 1024 char lineBuffer[MAX_LINE_LENGTH]; int lineParseOffset; char token[MAX_LINE_LENGTH]; int instructionCount; typedef struct { char *name; int opcode; } sourceOps_t; sourceOps_t sourceOps[] = { #include "opstrings.h" }; #define NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) ) int opcodesHash[ NUM_SOURCE_OPS ]; static int vreport (const char* fmt, va_list vp) { if (options.verbose != qtrue) return 0; return vprintf(fmt, vp); } static int report (const char *fmt, ...) { va_list va; int retval; va_start(va, fmt); retval = vreport(fmt, va); va_end(va); return retval; } /* The chain-and-bucket hash table. -PH */ static void hashtable_init (hashtable_t *H, int buckets) { H->buckets = buckets; H->table = calloc(H->buckets, sizeof(*(H->table))); return; } static hashtable_t *hashtable_new (int buckets) { hashtable_t *H; H = malloc(sizeof(hashtable_t)); hashtable_init(H, buckets); return H; } /* No destroy/destructor. No need. */ static void hashtable_add (hashtable_t *H, int hashvalue, void *datum) { hashchain_t *hc, **hb; hashvalue = (abs(hashvalue) % H->buckets); hb = &(H->table[hashvalue]); if (*hb == 0) { /* Empty bucket. Create new one. */ *hb = calloc(1, sizeof(**hb)); hc = *hb; } else { /* Get hc to point to last node in chain. */ for (hc = *hb; hc && hc->next; hc = hc->next); hc->next = calloc(1, sizeof(*hc)); hc = hc->next; } hc->data = datum; hc->next = 0; return; } static hashchain_t *hashtable_get (hashtable_t *H, int hashvalue) { hashvalue = (abs(hashvalue) % H->buckets); return (H->table[hashvalue]); } static void hashtable_stats (hashtable_t *H) { int len, empties, longest, nodes; int i; float meanlen; hashchain_t *hc; report("Stats for hashtable %08X", H); empties = 0; longest = 0; nodes = 0; for (i = 0; i < H->buckets; i++) { if (H->table[i] == 0) { empties++; continue; } for (hc = H->table[i], len = 0; hc; hc = hc->next, len++); if (len > longest) { longest = len; } nodes += len; } meanlen = (float)(nodes) / (H->buckets - empties); #if 0 /* Long stats display */ report(" Total buckets: %d\n", H->buckets); report(" Total stored nodes: %d\n", nodes); report(" Longest chain: %d\n", longest); report(" Empty chains: %d\n", empties); report(" Mean non-empty chain length: %f\n", meanlen); #else //0 /* Short stats display */ report(", %d buckets, %d nodes", H->buckets, nodes); report("\n"); report(" Longest chain: %d, empty chains: %d, mean non-empty: %f", longest, empties, meanlen); #endif //0 report("\n"); } /* Kludge. */ /* Check if symbol already exists. */ /* Returns 0 if symbol does NOT already exist, non-zero otherwise. */ static int hashtable_symbol_exists (hashtable_t *H, int hash, char *sym) { hashchain_t *hc; symbol_t *s; hash = (abs(hash) % H->buckets); hc = H->table[hash]; if (hc == 0) { /* Empty chain means this symbol has not yet been defined. */ return 0; } for (; hc; hc = hc->next) { s = (symbol_t*)hc->data; // if ((hash == s->hash) && (strcmp(sym, s->name) == 0)) /* We _already_ know the hash is the same. That's why we're probing! */ if (strcmp(sym, s->name) == 0) { /* Symbol collisions -- symbol already exists. */ return 1; } } return 0; /* Can't find collision. */ } /* Comparator function for quicksorting. */ static int symlist_cmp (const void *e1, const void *e2) { const symbol_t *a, *b; a = *(const symbol_t **)e1; b = *(const symbol_t **)e2; //crumb("Symbol comparison (1) %d to (2) %d\n", a->value, b->value); return ( a->value - b->value); } /* Sort the symbols list by using QuickSort (qsort()). This may take a LOT of memory (a few megabytes?), but memory is cheap these days. However, qsort(3) already exists, and I'm really lazy. -PH */ static void sort_symbols () { int i, elems; symbol_t *s; symbol_t **symlist; //crumb("sort_symbols: Constructing symlist array\n"); for (elems = 0, s = symbols; s; s = s->next, elems++) /* nop */ ; symlist = malloc(elems * sizeof(symbol_t*)); for (i = 0, s = symbols; s; s = s->next, i++) { symlist[i] = s; } //crumbf("sort_symbols: Quick-sorting %d symbols\n", elems); qsort(symlist, elems, sizeof(symbol_t*), symlist_cmp); //crumbf("sort_symbols: Reconstructing symbols list\n"); s = symbols = symlist[0]; for (i = 1; i < elems; i++) { s->next = symlist[i]; s = s->next; } lastSymbol = s; s->next = 0; //crumbf("sort_symbols: verifying..."); fflush(stdout); for (i = 0, s = symbols; s; s = s->next, i++) /*nop*/ ; //crumbf(" %d elements\n", i); free(symlist); /* d'oh. no gc. */ } #ifdef _MSC_VER #define INT64 __int64 #define atoi64 _atoi64 #else #define INT64 long long int #define atoi64 atoll #endif /* Problem: BYTE values are specified as signed decimal string. A properly functional atoip() will cap large signed values at 0x7FFFFFFF. Negative word values are often specified as very large decimal values by lcc. Therefore, values that should be between 0x7FFFFFFF and 0xFFFFFFFF come out as 0x7FFFFFFF when using atoi(). Bad. This function is one big evil hack to work around this problem. */ static int atoiNoCap (const char *s) { INT64 l; union { unsigned int u; signed int i; } retval; l = atoi64(s); /* Now smash to signed 32 bits accordingly. */ if (l < 0) { retval.i = (int)l; } else { retval.u = (unsigned int)l; } return retval.i; /* <- union hackage. I feel dirty with this. -PH */ } /* ============= HashString ============= */ /* Default hash function of Kazlib 1.19, slightly modified. */ static unsigned int HashString (const char *key) { static unsigned long randbox[] = { 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, }; const char *str = key; unsigned int acc = 0; while (*str) { acc ^= randbox[(*str + acc) & 0xf]; acc = (acc << 1) | (acc >> 31); acc &= 0xffffffffU; acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; acc = (acc << 2) | (acc >> 30); acc &= 0xffffffffU; } return abs(acc); } /* ============ CodeError ============ */ static void CodeError( char *fmt, ... ) { va_list argptr; errorCount++; report( "%s:%i ", currentFileName, currentFileLine ); va_start( argptr,fmt ); vprintf( fmt,argptr ); va_end( argptr ); } /* ============ EmitByte ============ */ static void EmitByte( segment_t *seg, int v ) { if ( seg->imageUsed >= MAX_IMAGE ) { Error( "MAX_IMAGE" ); } seg->image[ seg->imageUsed ] = v; seg->imageUsed++; } /* ============ EmitInt ============ */ static void EmitInt( segment_t *seg, int v ) { if ( seg->imageUsed >= MAX_IMAGE - 4) { Error( "MAX_IMAGE" ); } seg->image[ seg->imageUsed ] = v & 255; seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255; seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255; seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255; seg->imageUsed += 4; } /* ============ DefineSymbol Symbols can only be defined on pass 0 ============ */ static void DefineSymbol( char *sym, int value ) { /* Hand optimization by PhaethonH */ symbol_t *s; char expanded[MAX_LINE_LENGTH]; int hash; if ( passNumber == 1 ) { return; } // add the file prefix to local symbols to guarantee unique if ( sym[0] == '$' ) { sprintf( expanded, "%s_%i", sym, currentFileIndex ); sym = expanded; } hash = HashString( sym ); if (hashtable_symbol_exists(symtable, hash, sym)) { CodeError( "Multiple definitions for %s\n", sym ); return; } s = malloc( sizeof( *s ) ); s->next = NULL; s->name = copystring( sym ); s->hash = hash; s->value = value; s->segment = currentSegment; hashtable_add(symtable, hash, s); /* Hash table lookup already speeds up symbol lookup enormously. We postpone sorting until end of pass 0. Since we're not doing the insertion sort, lastSymbol should always wind up pointing to the end of list. This allows constant time for adding to the list. -PH */ if (symbols == 0) { lastSymbol = symbols = s; } else { lastSymbol->next = s; lastSymbol = s; } } /* ============ LookupSymbol Symbols can only be evaluated on pass 1 ============ */ static int LookupSymbol( char *sym ) { symbol_t *s; char expanded[MAX_LINE_LENGTH]; int hash; hashchain_t *hc; if ( passNumber == 0 ) { return 0; } // add the file prefix to local symbols to guarantee unique if ( sym[0] == '$' ) { sprintf( expanded, "%s_%i", sym, currentFileIndex ); sym = expanded; } hash = HashString( sym ); /* Hand optimization by PhaethonH Using a hash table with chain/bucket for lookups alone sped up q3asm by almost 3x for me. -PH */ for (hc = hashtable_get(symtable, hash); hc; hc = hc->next) { s = (symbol_t*)hc->data; /* ugly typecasting, but it's fast! */ if ( (hash == s->hash) && !strcmp(sym, s->name) ) { return s->segment->segmentBase + s->value; } } CodeError( "error: symbol %s undefined\n", sym ); passNumber = 0; DefineSymbol( sym, 0 ); // so more errors aren't printed passNumber = 1; return 0; } /* ============== ExtractLine Extracts the next line from the given text block. If a full line isn't parsed, returns NULL Otherwise returns the updated parse pointer =============== */ static char *ExtractLine( char *data ) { /* Goal: Given a string `data', extract one text line into buffer `lineBuffer' that is no longer than MAX_LINE_LENGTH characters long. Return value is remainder of `data' that isn't part of `lineBuffer'. -PH */ /* Hand-optimized by PhaethonH */ char *p, *q; currentFileLine++; lineParseOffset = 0; token[0] = 0; *lineBuffer = 0; p = q = data; if (!*q) { return NULL; } for ( ; !((*p == 0) || (*p == '\n')); p++) /* nop */ ; if ((p - q) >= MAX_LINE_LENGTH) { CodeError( "MAX_LINE_LENGTH" ); return data; } memcpy( lineBuffer, data, (p - data) ); lineBuffer[(p - data)] = 0; p += (*p == '\n') ? 1 : 0; /* Skip over final newline. */ return p; } /* ============== Parse Parse a token out of linebuffer ============== */ static qboolean Parse( void ) { /* Hand-optimized by PhaethonH */ const char *p, *q; /* Because lineParseOffset is only updated just before exit, this makes this code version somewhat harder to debug under a symbolic debugger. */ *token = 0; /* Clear token. */ // skip whitespace for (p = lineBuffer + lineParseOffset; *p && (*p <= ' '); p++) /* nop */ ; // skip ; comments /* die on end-of-string */ if ((*p == ';') || (*p == 0)) { lineParseOffset = p - lineBuffer; return qfalse; } q = p; /* Mark the start of token. */ /* Find separator first. */ for ( ; *p > 32; p++) /* nop */ ; /* XXX: unsafe assumptions. */ /* *p now sits on separator. Mangle other values accordingly. */ strncpy(token, q, p - q); token[p - q] = 0; lineParseOffset = p - lineBuffer; return qtrue; } /* ============== ParseValue ============== */ static int ParseValue( void ) { Parse(); return atoiNoCap( token ); } /* ============== ParseExpression ============== */ static int ParseExpression(void) { /* Hand optimization, PhaethonH */ int i, j; char sym[MAX_LINE_LENGTH]; int v; /* Skip over a leading minus. */ for ( i = ((token[0] == '-') ? 1 : 0) ; i < MAX_LINE_LENGTH ; i++ ) { if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) { break; } } memcpy( sym, token, i ); sym[i] = 0; switch (*sym) { /* Resolve depending on first character. */ /* Optimizing compilers can convert cases into "calculated jumps". I think these are faster. -PH */ case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v = atoiNoCap(sym); break; default: v = LookupSymbol(sym); break; } // parse add / subtract offsets while ( token[i] != 0 ) { for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) { if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) { break; } } memcpy( sym, token+i+1, j-i-1 ); sym[j-i-1] = 0; switch (token[i]) { case '+': v += atoiNoCap(sym); break; case '-': v -= atoiNoCap(sym); break; } i = j; } return v; } /* ============== HackToSegment BIG HACK: I want to put all 32 bit values in the data segment so they can be byte swapped, and all char data in the lit segment, but switch jump tables are emited in the lit segment and initialized strng variables are put in the data segment. I can change segments here, but I also need to fixup the label that was just defined Note that the lit segment is read-write in the VM, so strings aren't read only as in some architectures. ============== */ static void HackToSegment( segmentName_t seg ) { if ( currentSegment == &segment[seg] ) { return; } currentSegment = &segment[seg]; if ( passNumber == 0 ) { lastSymbol->segment = currentSegment; lastSymbol->value = currentSegment->imageUsed; } } //#define STAT(L) report("STAT " L "\n"); #define STAT(L) #define ASM(O) int TryAssemble##O () /* These clauses were moved out from AssembleLine() to allow reordering of if's. An optimizing compiler should reconstruct these back into inline code. -PH */ // call instructions reset currentArgOffset ASM(CALL) { if ( !strncmp( token, "CALL", 4 ) ) { STAT("CALL"); EmitByte( &segment[CODESEG], OP_CALL ); instructionCount++; currentArgOffset = 0; return 1; } return 0; } // arg is converted to a reversed store ASM(ARG) { if ( !strncmp( token, "ARG", 3 ) ) { STAT("ARG"); EmitByte( &segment[CODESEG], OP_ARG ); instructionCount++; if ( 8 + currentArgOffset >= 256 ) { CodeError( "currentArgOffset >= 256" ); return 1; } EmitByte( &segment[CODESEG], 8 + currentArgOffset ); currentArgOffset += 4; return 1; } return 0; } // ret just leaves something on the op stack ASM(RET) { if ( !strncmp( token, "RET", 3 ) ) { STAT("RET"); EmitByte( &segment[CODESEG], OP_LEAVE ); instructionCount++; EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); return 1; } return 0; } // pop is needed to discard the return value of // a function ASM(POP) { if ( !strncmp( token, "pop", 3 ) ) { STAT("POP"); EmitByte( &segment[CODESEG], OP_POP ); instructionCount++; return 1; } return 0; } // address of a parameter is converted to OP_LOCAL ASM(ADDRF) { int v; if ( !strncmp( token, "ADDRF", 5 ) ) { STAT("ADDRF"); instructionCount++; Parse(); v = ParseExpression(); v = 16 + currentArgs + currentLocals + v; EmitByte( &segment[CODESEG], OP_LOCAL ); EmitInt( &segment[CODESEG], v ); return 1; } return 0; } // address of a local is converted to OP_LOCAL ASM(ADDRL) { int v; if ( !strncmp( token, "ADDRL", 5 ) ) { STAT("ADDRL"); instructionCount++; Parse(); v = ParseExpression(); v = 8 + currentArgs + v; EmitByte( &segment[CODESEG], OP_LOCAL ); EmitInt( &segment[CODESEG], v ); return 1; } return 0; } ASM(PROC) { char name[1024]; if ( !strcmp( token, "proc" ) ) { STAT("PROC"); Parse(); // function name strcpy( name, token ); DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed ); currentLocals = ParseValue(); // locals currentLocals = ( currentLocals + 3 ) & ~3; currentArgs = ParseValue(); // arg marshalling currentArgs = ( currentArgs + 3 ) & ~3; if ( 8 + currentLocals + currentArgs >= 32767 ) { CodeError( "Locals > 32k in %s\n", name ); } instructionCount++; EmitByte( &segment[CODESEG], OP_ENTER ); EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); return 1; } return 0; } ASM(ENDPROC) { int v, v2; if ( !strcmp( token, "endproc" ) ) { STAT("ENDPROC"); Parse(); // skip the function name v = ParseValue(); // locals v2 = ParseValue(); // arg marshalling // all functions must leave something on the opstack instructionCount++; EmitByte( &segment[CODESEG], OP_PUSH ); instructionCount++; EmitByte( &segment[CODESEG], OP_LEAVE ); EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs ); return 1; } return 0; } ASM(ADDRESS) { int v; if ( !strcmp( token, "address" ) ) { STAT("ADDRESS"); Parse(); v = ParseExpression(); /* Addresses are 32 bits wide, and therefore go into data segment. */ HackToSegment( DATASEG ); EmitInt( currentSegment, v ); if( passNumber == 1 && token[ 0 ] == '$' ) // crude test for labels EmitInt( &segment[ JTRGSEG ], v ); return 1; } return 0; } ASM(EXPORT) { if ( !strcmp( token, "export" ) ) { STAT("EXPORT"); return 1; } return 0; } ASM(IMPORT) { if ( !strcmp( token, "import" ) ) { STAT("IMPORT"); return 1; } return 0; } ASM(CODE) { if ( !strcmp( token, "code" ) ) { STAT("CODE"); currentSegment = &segment[CODESEG]; return 1; } return 0; } ASM(BSS) { if ( !strcmp( token, "bss" ) ) { STAT("BSS"); currentSegment = &segment[BSSSEG]; return 1; } return 0; } ASM(DATA) { if ( !strcmp( token, "data" ) ) { STAT("DATA"); currentSegment = &segment[DATASEG]; return 1; } return 0; } ASM(LIT) { if ( !strcmp( token, "lit" ) ) { STAT("LIT"); currentSegment = &segment[LITSEG]; return 1; } return 0; } ASM(LINE) { if ( !strcmp( token, "line" ) ) { STAT("LINE"); return 1; } return 0; } ASM(FILE) { if ( !strcmp( token, "file" ) ) { STAT("FILE"); return 1; } return 0; } ASM(EQU) { char name[1024]; if ( !strcmp( token, "equ" ) ) { STAT("EQU"); Parse(); strcpy( name, token ); Parse(); DefineSymbol( name, atoiNoCap(token) ); return 1; } return 0; } ASM(ALIGN) { int v; if ( !strcmp( token, "align" ) ) { STAT("ALIGN"); v = ParseValue(); currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 ); return 1; } return 0; } ASM(SKIP) { int v; if ( !strcmp( token, "skip" ) ) { STAT("SKIP"); v = ParseValue(); currentSegment->imageUsed += v; return 1; } return 0; } ASM(BYTE) { int i, v, v2; if ( !strcmp( token, "byte" ) ) { STAT("BYTE"); v = ParseValue(); v2 = ParseValue(); if ( v == 1 ) { /* Character (1-byte) values go into lit(eral) segment. */ HackToSegment( LITSEG ); } else if ( v == 4 ) { /* 32-bit (4-byte) values go into data segment. */ HackToSegment( DATASEG ); } else if ( v == 2 ) { /* and 16-bit (2-byte) values will cause q3asm to barf. */ CodeError( "16 bit initialized data not supported" ); } // emit little endien for ( i = 0 ; i < v ; i++ ) { EmitByte( currentSegment, (v2 & 0xFF) ); /* paranoid ANDing -PH */ v2 >>= 8; } return 1; } return 0; } // code labels are emited as instruction counts, not byte offsets, // because the physical size of the code will change with // different run time compilers and we want to minimize the // size of the required translation table ASM(LABEL) { if ( !strncmp( token, "LABEL", 5 ) ) { STAT("LABEL"); Parse(); if ( currentSegment == &segment[CODESEG] ) { DefineSymbol( token, instructionCount ); } else { DefineSymbol( token, currentSegment->imageUsed ); } return 1; } return 0; } /* ============== AssembleLine ============== */ static void AssembleLine( void ) { hashchain_t *hc; sourceOps_t *op; int i; int hash; Parse(); if ( !token[0] ) { return; } hash = HashString( token ); /* Opcode search using hash table. Since the opcodes stays mostly fixed, this may benefit even more from a tree. Always with the tree :) -PH */ for (hc = hashtable_get(optable, hash); hc; hc = hc->next) { op = (sourceOps_t*)(hc->data); i = op - sourceOps; if ((hash == opcodesHash[i]) && (!strcmp(token, op->name))) { int opcode; int expression; if ( op->opcode == OP_UNDEF ) { CodeError( "Undefined opcode: %s\n", token ); } if ( op->opcode == OP_IGNORE ) { return; // we ignore most conversions } // sign extensions need to check next parm opcode = op->opcode; if ( opcode == OP_SEX8 ) { Parse(); if ( token[0] == '1' ) { opcode = OP_SEX8; } else if ( token[0] == '2' ) { opcode = OP_SEX16; } else { CodeError( "Bad sign extension: %s\n", token ); return; } } // check for expression Parse(); if ( token[0] && op->opcode != OP_CVIF && op->opcode != OP_CVFI ) { expression = ParseExpression(); // code like this can generate non-dword block copies: // auto char buf[2] = " "; // we are just going to round up. This might conceivably // be incorrect if other initialized chars follow. if ( opcode == OP_BLOCK_COPY ) { expression = ( expression + 3 ) & ~3; } EmitByte( &segment[CODESEG], opcode ); EmitInt( &segment[CODESEG], expression ); } else { EmitByte( &segment[CODESEG], opcode ); } instructionCount++; return; } } /* This falls through if an assembly opcode is not found. -PH */ /* The following should be sorted in sequence of statistical frequency, most frequent first. -PH */ /* Empirical frequency statistics from FI 2001.01.23: 109892 STAT ADDRL 72188 STAT BYTE 51150 STAT LINE 50906 STAT ARG 43704 STAT IMPORT 34902 STAT LABEL 32066 STAT ADDRF 23704 STAT CALL 7720 STAT POP 7256 STAT RET 5198 STAT ALIGN 3292 STAT EXPORT 2878 STAT PROC 2878 STAT ENDPROC 2812 STAT ADDRESS 738 STAT SKIP 374 STAT EQU 280 STAT CODE 176 STAT LIT 102 STAT FILE 100 STAT BSS 68 STAT DATA -PH */ #undef ASM #define ASM(O) if (TryAssemble##O ()) return; ASM(ADDRL) ASM(BYTE) ASM(LINE) ASM(ARG) ASM(IMPORT) ASM(LABEL) ASM(ADDRF) ASM(CALL) ASM(POP) ASM(RET) ASM(ALIGN) ASM(EXPORT) ASM(PROC) ASM(ENDPROC) ASM(ADDRESS) ASM(SKIP) ASM(EQU) ASM(CODE) ASM(LIT) ASM(FILE) ASM(BSS) ASM(DATA) CodeError( "Unknown token: %s\n", token ); } /* ============== InitTables ============== */ void InitTables( void ) { int i; symtable = hashtable_new(symtablelen); optable = hashtable_new(100); /* There's hardly 100 opcodes anyway. */ for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) { opcodesHash[i] = HashString( sourceOps[i].name ); hashtable_add(optable, opcodesHash[i], sourceOps + i); } } /* ============== WriteMapFile ============== */ static void WriteMapFile( void ) { FILE *f; symbol_t *s; char imageName[MAX_OS_PATH]; int seg; strcpy( imageName, outputFilename ); StripExtension( imageName ); strcat( imageName, ".map" ); report( "Writing %s...\n", imageName ); f = SafeOpenWrite( imageName ); for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) { for ( s = symbols ; s ; s = s->next ) { if ( s->name[0] == '$' ) { continue; // skip locals } if ( &segment[seg] != s->segment ) { continue; } fprintf( f, "%i %8x %s\n", seg, s->value, s->name ); } } fclose( f ); } /* =============== WriteVmFile =============== */ static void WriteVmFile( void ) { char imageName[MAX_OS_PATH]; vmHeader_t header; FILE *f; int headerSize; report( "%i total errors\n", errorCount ); strcpy( imageName, outputFilename ); StripExtension( imageName ); strcat( imageName, ".qvm" ); remove( imageName ); report( "code segment: %7i\n", segment[CODESEG].imageUsed ); report( "data segment: %7i\n", segment[DATASEG].imageUsed ); report( "lit segment: %7i\n", segment[LITSEG].imageUsed ); report( "bss segment: %7i\n", segment[BSSSEG].imageUsed ); report( "instruction count: %i\n", instructionCount ); if ( errorCount != 0 ) { report( "Not writing a file due to errors\n" ); return; } if( !options.vanillaQ3Compatibility ) { header.vmMagic = VM_MAGIC_VER2; headerSize = sizeof( header ); } else { header.vmMagic = VM_MAGIC; // Don't write the VM_MAGIC_VER2 bits when maintaining 1.32b compatibility. // (I know this isn't strictly correct due to padding, but then platforms // that pad wouldn't be able to write a correct header anyway). Note: if // vmHeader_t changes, this needs to be adjusted too. headerSize = sizeof( header ) - sizeof( header.jtrgLength ); } header.instructionCount = instructionCount; header.codeOffset = headerSize; header.codeLength = segment[CODESEG].imageUsed; header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed; header.dataLength = segment[DATASEG].imageUsed; header.litLength = segment[LITSEG].imageUsed; header.bssLength = segment[BSSSEG].imageUsed; header.jtrgLength = segment[JTRGSEG].imageUsed; report( "Writing to %s\n", imageName ); #ifdef Q3_BIG_ENDIAN { int i; // byte swap the header for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) { ((int *)&header)[i] = LittleLong( ((int *)&header)[i] ); } } #endif CreatePath( imageName ); f = SafeOpenWrite( imageName ); SafeWrite( f, &header, headerSize ); SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed ); SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed ); SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed ); if( !options.vanillaQ3Compatibility ) { SafeWrite( f, &segment[JTRGSEG].image, segment[JTRGSEG].imageUsed ); } fclose( f ); } /* =============== Assemble =============== */ static void Assemble( void ) { int i; char filename[MAX_OS_PATH]; char *ptr; report( "outputFilename: %s\n", outputFilename ); for ( i = 0 ; i < numAsmFiles ; i++ ) { strcpy( filename, asmFileNames[ i ] ); DefaultExtension( filename, ".asm" ); LoadFile( filename, (void **)&asmFiles[i] ); } // assemble for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) { segment[LITSEG].segmentBase = segment[DATASEG].imageUsed; segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed; segment[JTRGSEG].segmentBase = segment[BSSSEG].segmentBase + segment[BSSSEG].imageUsed; for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { segment[i].imageUsed = 0; } segment[DATASEG].imageUsed = 4; // skip the 0 byte, so NULL pointers are fixed up properly instructionCount = 0; for ( i = 0 ; i < numAsmFiles ; i++ ) { currentFileIndex = i; currentFileName = asmFileNames[ i ]; currentFileLine = 0; report("pass %i: %s\n", passNumber, currentFileName ); fflush( NULL ); ptr = asmFiles[i]; while ( ptr ) { ptr = ExtractLine( ptr ); AssembleLine(); } } // align all segment for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) { segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3; } if (passNumber == 0) { sort_symbols(); } } // reserve the stack in bss DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed ); segment[BSSSEG].imageUsed += stackSize; DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed ); // write the image WriteVmFile(); // write the map file even if there were errors if( options.writeMapFile ) { WriteMapFile(); } } /* ============= ParseOptionFile ============= */ static void ParseOptionFile( const char *filename ) { char expanded[MAX_OS_PATH]; char *text, *text_p; strcpy( expanded, filename ); DefaultExtension( expanded, ".q3asm" ); LoadFile( expanded, (void **)&text ); if ( !text ) { return; } text_p = text; while( ( text_p = COM_Parse( text_p ) ) != 0 ) { if ( !strcmp( com_token, "-o" ) ) { // allow output override in option file text_p = COM_Parse( text_p ); if ( text_p ) { strcpy( outputFilename, com_token ); } continue; } asmFileNames[ numAsmFiles ] = copystring( com_token ); numAsmFiles++; } } static void ShowHelp( char *argv0 ) { Error("Usage: %s [OPTION]... [FILES]...\n\ Assemble LCC bytecode assembly to Q3VM bytecode.\n\ \n\ -o OUTPUT Write assembled output to file OUTPUT.qvm\n\ -f LISTFILE Read options and list of files to assemble from LISTFILE.q3asm\n\ -b BUCKETS Set symbol hash table to BUCKETS buckets\n\ -v Verbose compilation report\n\ -vq3 Produce a qvm file compatible with Q3 1.32b\n\ -h --help -? Show this help\n\ ", argv0); } /* ============== main ============== */ int main( int argc, char **argv ) { int i; double start, end; // _chdir( "/quake3/jccode/cgame/lccout" ); // hack for vc profiler if ( argc < 2 ) { ShowHelp( argv[0] ); } start = I_FloatTime (); // default filename is "q3asm" strcpy( outputFilename, "q3asm" ); numAsmFiles = 0; for ( i = 1 ; i < argc ; i++ ) { if ( argv[i][0] != '-' ) { break; } if( !strcmp( argv[ i ], "-h" ) || !strcmp( argv[ i ], "--help" ) || !strcmp( argv[ i ], "-?") ) { ShowHelp( argv[0] ); } if ( !strcmp( argv[i], "-o" ) ) { if ( i == argc - 1 ) { Error( "-o must preceed a filename" ); } /* Timbo of Tremulous pointed out -o not working; stock ID q3asm folded in the change. Yay. */ strcpy( outputFilename, argv[ i+1 ] ); i++; continue; } if ( !strcmp( argv[i], "-f" ) ) { if ( i == argc - 1 ) { Error( "-f must preceed a filename" ); } ParseOptionFile( argv[ i+1 ] ); i++; continue; } if (!strcmp(argv[i], "-b")) { if (i == argc - 1) { Error("-b requires an argument"); } i++; symtablelen = atoiNoCap(argv[i]); continue; } if( !strcmp( argv[ i ], "-v" ) ) { /* Verbosity option added by Timbo, 2002.09.14. By default (no -v option), q3asm remains silent except for critical errors. Verbosity turns on all messages, error or not. Motivation: not wanting to scrollback for pages to find asm error. */ options.verbose = qtrue; continue; } if( !strcmp( argv[ i ], "-m" ) ) { options.writeMapFile = qtrue; continue; } if( !strcmp( argv[ i ], "-vq3" ) ) { options.vanillaQ3Compatibility = qtrue; continue; } Error( "Unknown option: %s", argv[i] ); } // the rest of the command line args are asm files for ( ; i < argc ; i++ ) { asmFileNames[ numAsmFiles ] = copystring( argv[ i ] ); numAsmFiles++; } // In some case it Segfault without this check if ( numAsmFiles == 0 ) { Error( "No file to assemble\n" ); } InitTables(); Assemble(); { symbol_t *s; for ( i = 0, s = symbols ; s ; s = s->next, i++ ) /* nop */ ; if (options.verbose) { report("%d symbols defined\n", i); hashtable_stats(symtable); hashtable_stats(optable); } } end = I_FloatTime (); report ("%5.0f seconds elapsed\n", end-start); return errorCount; } openarena_0.8.8.orig/code/tools/asm/lib.txt0000644000175000017500000000023211656310262017362 0ustar smcvsmcv strlen strcasecmp tolower strcat strncpy strcmp strcpy strchr vsprintf memcpy memset rand atoi atof abs floor fabs tan atan sqrt log cos sin atan2 openarena_0.8.8.orig/code/tools/asm/cmdlib.h0000644000175000017500000001024111656310262017457 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cmdlib.h #ifndef __CMDLIB__ #define __CMDLIB__ #ifdef _MSC_VER #pragma warning(disable : 4244) // MIPS #pragma warning(disable : 4136) // X86 #pragma warning(disable : 4051) // ALPHA #pragma warning(disable : 4018) // signed/unsigned mismatch #pragma warning(disable : 4305) // truncate from double to float #pragma check_stack(off) #endif #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma intrinsic( memset, memcpy ) #endif #ifndef __BYTEBOOL__ #define __BYTEBOOL__ typedef enum { qfalse, qtrue } qboolean; typedef unsigned char byte; #endif #define MAX_OS_PATH 1024 #define MEM_BLOCKSIZE 4096 // the dec offsetof macro doesnt work very well... #define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) // set these before calling CheckParm extern int myargc; extern char **myargv; char *strupr (char *in); char *strlower (char *in); int Q_strncasecmp( const char *s1, const char *s2, int n ); int Q_stricmp( const char *s1, const char *s2 ); #define Q_strequal(s1,s2) (Q_stricmp(s1,s2)==0) void Q_getwd( char *out ); int Q_filelength (FILE *f); int FileTime( const char *path ); void Q_mkdir( const char *path ); extern char qdir[1024]; extern char gamedir[1024]; extern char writedir[1024]; void SetQdirFromPath( const char *path ); char *ExpandArg( const char *path ); // from cmd line char *ExpandPath( const char *path ); // from scripts char *ExpandGamePath (const char *path); char *ExpandPathAndArchive( const char *path ); double I_FloatTime( void ); void Error( const char *error, ... ); int CheckParm( const char *check ); FILE *SafeOpenWrite( const char *filename ); FILE *SafeOpenRead( const char *filename ); void SafeRead (FILE *f, void *buffer, int count); void SafeWrite (FILE *f, const void *buffer, int count); int LoadFile( const char *filename, void **bufferptr ); int LoadFileBlock( const char *filename, void **bufferptr ); int TryLoadFile( const char *filename, void **bufferptr ); void SaveFile( const char *filename, const void *buffer, int count ); qboolean FileExists( const char *filename ); void DefaultExtension( char *path, const char *extension ); void DefaultPath( char *path, const char *basepath ); void StripFilename( char *path ); void StripExtension( char *path ); void ExtractFilePath( const char *path, char *dest ); void ExtractFileBase( const char *path, char *dest ); void ExtractFileExtension( const char *path, char *dest ); int ParseNum (const char *str); char *COM_Parse (char *data); extern char com_token[1024]; extern qboolean com_eof; char *copystring(const char *s); void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); void CreatePath( const char *path ); void QCopyFile( const char *from, const char *to ); extern qboolean archive; extern char archivedir[1024]; extern qboolean verbose; void qprintf( const char *format, ... ); void _printf( const char *format, ... ); void ExpandWildcards( int *argc, char ***argv ); // for compression routines typedef struct { void *data; int count, width, height; } cblock_t; #endif openarena_0.8.8.orig/code/tools/lcc/0000755000175000017500000000000011720470205016033 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/LOG0000644000175000017500000000621611656310263016411 0ustar smcvsmcvFrom lcc 4.0 to 4.1: Changes: See doc/4.html for changes in the code-generation interface. Warns about constants that are too large, eg, short x = 70000; Warns about expressions that have no effect. Unsigned shorts are now used for wide-character constants, and wchar_t is a typedef for unsigned short. More assertions in gen.c to confirm that the register allocator is configured correctly; ie, that the various masks, wildcards, clobbers, and targets are internally consistent. Full checking appears impractical, but there's still more than than there was before. On the SPARC, lcc now emits .type and .size directives unconditionally. On the x86, constants are now emitted into the text segment. If the environment variable "LCCDIR" is defined, it gives the directory that contains the preprocessor, the compiler proper, and the lcc-specific libraries. Under Windows, lcc searches the directories named in the environment variable "include" for header files. Errors fixed: Erroneously complained about unknown sizes for some const fields, eg, typedef struct foo ref; struct foo { const ref *q; int a; }; f(ref *p, int i) { return p->q[i].a; } -A -A erroneously complained about static main's that didn't conform to the ANSI-mandated "int main(void)" or "int main(int, char **)". Silently generated incorrect code for a structure copy with a post-incremented target, eg, struct { int x; } data = {1}, copy[2], *q = copy; main() { *q++ = data; } Generated incorrect values in some expressions with constant pointers. Silently truncated string literals longer than 4095 characters. Failed to emit debugging information for uninitialized globals. Failed to diagnose missing sizes in some multi-dimensioned array declarators, eg, extern int x[][10]; int x[5][]; Silently emitted incorrect sizes and initalizations for some incomplete multi-dimensioned arrays involving pointers and whose size is determined by the number of initializers. Set only the x.name field for some back-end symbols (eg, wildcards), and the uninitialized name field crashed some debugging output. uses() failed to check the register *set* as well as the register mask. There's no known bug demo, but a wildcard set might be contrived that would need the test. Crashed with -b on some conditional expressions involving calls, eg, int p; void g(void) { p ? f() : 1; } On the MIPS, sometimes generated an incorrect frame size and thus a crash when floating-point registers were saved. On the SPARC, erroneously reused a register variable as a temporary when the variable is compiler-generated. On the SPARC with -b, emitted incorrect code for returning structs. On the x86, conversion from float to int rounded instead of truncated with the default floating-point mode. On the x86, eliminate rtargets for kids after the first (see p. 419). On the x86, substitute reg for freg, in order to use the common reg rules. Needed only for debugging output, since we're not using any float regs as regs at this time. On the x86, "double f(); main(){f();}" wasn't popping the FP register stack. On the x86, ECX was saved by the callee, when it should have been saved by the caller. $Id: LOG 145 2001-10-17 21:53:10Z timo $ openarena_0.8.8.orig/code/tools/lcc/doc/0000755000175000017500000000000011720470210016574 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/doc/bprint.pdf0000644000175000017500000001154311656310262020601 0ustar smcvsmcv%PDF-1.2 %âãÏÓ 3 0 obj << /Linearized 1 /O 6 /H [ 728 150 ] /L 4963 /E 4647 /N 1 /T 4786 >> endobj xref 3 13 0000000016 00000 n 0000000604 00000 n 0000000676 00000 n 0000000878 00000 n 0000001029 00000 n 0000001152 00000 n 0000002521 00000 n 0000002627 00000 n 0000002733 00000 n 0000002838 00000 n 0000004505 00000 n 0000000728 00000 n 0000000858 00000 n trailer << /Size 16 /Info 2 0 R /Root 4 0 R /Prev 4777 /ID[<07a7fb3c9c94cd11d87cb14f746f4b28><07a7fb3c9c94cd11d87cb14f746f4b28>] >> startxref 0 %%EOF 4 0 obj << /Type /Catalog /Pages 1 0 R /OpenAction 5 0 R >> endobj 5 0 obj << /S /GoTo /D [ 6 0 R /Fit ] >> endobj 14 0 obj << /S 36 /Filter /FlateDecode /Length 15 0 R >> stream H‰c``àf``ºÁ|• Ø”€²!|?nN…m­,Ø>®``Vj endstream endobj 15 0 obj 46 endobj 6 0 obj << /Type /Page /Parent 1 0 R /Resources 7 0 R /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 7 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 9 0 R /F4 11 0 R /F6 10 0 R >> /ExtGState << /GS1 13 0 R >> >> endobj 8 0 obj << /Type /Encoding /Differences [ 0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle 94 /circumflex 126 /tilde 128 /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /ff /fi /fl /ffi /ffl /dotlessi /dotlessj /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis ] >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /Encoding 8 0 R /BaseFont /Times-Italic >> endobj 10 0 obj << /Type /Font /Subtype /Type1 /Name /F6 /Encoding 8 0 R /BaseFont /Times-Roman >> endobj 11 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /Encoding 8 0 R /BaseFont /Times-Bold >> endobj 12 0 obj << /Length 1592 /Filter /FlateDecode >> stream H‰œW[oÛ6~ÿÀ‡•‡˜%êÖ·6I mÔÞÃ-Ñ[™òDyi6ì¿ïêfKqÖ `Ù¼œÛ÷}çèÍâôäümHY¬NO˜Kð>"Dœ“ÅæôÄ%kØónÎÈÚà·Ezzâ¼¹ù<»^Ü9ìnB&‹¯§'Ü£nÃáÅåá²]=ˉµXnl Ü:×Ïu^¼š|Y¼Ú{Ã$lZ<à©å¶Tº"w^9aó}[JcT¡É¶,î<ÆsY¶W>a?ŒêÙùoןnæ³¹uv`5ŒÚ]µÁ& ÖG&‘߯}Û,zÍ¢Kƒ¸[,¶úF)\Á©„Í.—zAâ/ÃËea·­ÞSùÄ•<¯5üåx ÂÀ¥u™Ë«ùÅçÙÍböézhø0!~Ü9’Ⱥh\îÙr9P’l—JC Õ½$¦:eFŠ]µÝUD\™Jé5)VvX—bcHZl¶*—Y>óbÚc.OÓ'Œò [~PÕ=Þ;È…âí„Òr| ‹ûâ$^ƒ¾ºš”\}GèÉi ´©"2eš:q“tgÃzà§ÌMZ…|Œ2Ê#Ön0EŸ±ÝJ™îx}&*Aªb`ƒ4j‹r µ˜°Ø)Vè¹µ7àO¹Ÿt»édÄž³€š •ÒTpÿ· s©±XR¤÷C*[ä&'“ƒœDmÛ¢+ŒB¦2³u¶Ð»ÍR–jÀQy@«Â4Ölâ!«pDê4/ <€a¡×À‰e)ÒÚÉÊ8’ÉJ–¥aϪ,6µk‹_Z9qТÍׇ³ð§rçБDA»v„mcr.OX¸'èìéÈÄÆ=njq:#÷ RÀååƒ2’¬Ë:C,'%~ùÓÖ²|T¡æz.M¼øyưqºù!0&hHü>A·Îí4±¥‹J âZàZh™´TK$ä²@×ëh&®3.Þ:0¬Wf°ÞeŒé–¦áVëÀ8º±…ÕQ (ÐICÅ:ÈBçCú#¸ AA“.RaQ×ñ~µÓ©B ž8¯'SÀžcdZèlT·B7ÝÛ¡c€\·k[f·ÙˆRýÔOEž£*Tm*)2{pêE4÷•¥æ28‡' Y?’Ùˆ¤IÒµ„L•cŠº=2ÌV¦ ‘®‰,S˜ ‘8&Óª(•õ¸¡@U#å7Rõê¡`Ñ»Àu”}¦ŽÓ Œ’ˆÕ½¨HV€.YZ‚­Ñ'$ßïç/iÛ¹ƒýä6Ö`c¶Â¾''Sßeíà¡ ³(åA,ú¬GQ)ÿØIƒ„±âº*Jø”ºK¶JX‹7ž)Ê €‡’$ñg„VÀÐ]Ô¾ºÃŠ·íø)_†©Œ¨Ïãç;¾u TØ<‰èÆÑ)Ї.­gYçíìÃÕ|xáÁüÄyÜlŽhèEÿÐÂWXÉý^^ãó6 Ûyñáââröù> endobj 1 0 obj << /Type /Pages /Kids [ 6 0 R ] /Count 1 >> endobj 2 0 obj << /CreationDate (D:19980824141908) /Producer (Acrobat Distiller 3.0 for Windows) /ModDate (D:19980824141937) >> endobj xref 0 3 0000000000 65535 f 0000004583 00000 n 0000004647 00000 n trailer << /Size 3 /ID[<07a7fb3c9c94cd11d87cb14f746f4b28><07a7fb3c9c94cd11d87cb14f746f4b28>] >> startxref 173 %%EOF openarena_0.8.8.orig/code/tools/lcc/doc/bprint.10000644000175000017500000000333011656310262020163 0ustar smcvsmcv.\" $Id: bprint.1 145 2001-10-17 21:53:10Z timo $ .TH BPRINT 1 "local \- $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $" .SH NAME bprint \- expression profiler .SH SYNOPSIS .B bprint [ .I option ... ] [ .I file ... ] .SH DESCRIPTION .I bprint produces on the standard output a listing of the programs compiled by .I lcc with the .B \-b option. Executing an .B a.out so compiled appends profiling data to .BR prof.out . The first token of each expression in the listing is preceded by the number of times it was executed enclosed in angle brackets as determined from the data in .BR prof.out . .I bprint interprets the following options. .TP .B \-c Compress the .B prof.out file, which otherwise grows with every execution of .BR a.out . .TP .B \-b Print an annotated listing as described above. .TP .B \-n Include line numbers in the listing. .TP .B \-f Print only the number of invocations of each function. A second .B \-f summarizes call sites instead of callers. .TP .BI \-I \*Sdir specifies additional directories in which to seek files given in .B prof.out that do not begin with `/'. .PP If any file names are given, only the requested data for those files are printed in the order presented. If no options are given, .B \-b is assumed. .SH FILES .PP .ta \w'$LCCDIR/liblcc.{a,lib}XX'u .nf prof.out profiling data $LCCDIR/liblcc.{a,lib} \fIlcc\fP-specific library .SH "SEE ALSO" .IR lcc (1), .IR prof (1) .SH BUGS Macros and comments can confuse .I bprint because it uses post-expansion source coordinates to annotate pre-expansion source files. If .I bprint sees that it's about to print a statement count .I inside a number or identifier, it moves the count to just .I before the token. .PP Can't cope with an ill-formed .BR prof.out . openarena_0.8.8.orig/code/tools/lcc/doc/install.html0000644000175000017500000013035511656310262021147 0ustar smcvsmcv Installing lcc

Installing lcc

Christopher W. Fraser and David R. Hanson, Microsoft Research

Contents

  • Introduction
  • Installation on UNIX
  • Building the Driver
  • Building the Compiler and Accessories
  • Installation on Windows NT 4.0 and Windows 95/98
  • Reporting Bugs
  • Keeping in Touch
  • Introduction

    lcc is the ANSI C compiler described in our book A Retargetable C Compiler: Design and Implementation (Addison-Wesley, 1995, ISBN 0-8053-1670-1).

    If you're installing lcc on a UNIX system, read the remainder of this section and continue with the next section. If you're installing lcc on a Windows NT 4.0 or Windows 95/98 system, and you intend only to use lcc, you can run the InstallShield executable, which installs the binaries and the documentation. If you want to modify lcc or rebuild it from the source files, you need the complete distribution, and you should read the rest of the section, the following three sections, and the Windows NT/95/98 section.

    Extract the distribution into its own directory. All non-absolute paths below are relative to this directory. The distribution holds the following subdirectories.

    src source code
    etc driver, accessories
    lib runtime library source code
    cpp preprocessor source code
    lburg code-generator generator source code
    doc this document, man pages
    include/*/* include files
    tst test suite
    alpha/*/tst ALPHA test outputs
    mips/*/tst MIPS test outputs
    sparc/*/tst SPARC test outputs
    x86/*/tst X86 test outputs

    doc/install.html is the HTML file for this document. doc/4.html describes the internal differences between lcc 3.x and 4.1.

    The installation makefile is designed so that lcc can be installed from a read-only file system or directory, which is common in networked environments, so the distribution can be unloaded on a central file server. You will need an existing ANSI/ISO C compiler to build and install lcc.

    Installation on UNIX

    The compilation components (the preprocessor, include files, and compiler proper, etc.) are installed in a single build directory. On multi-platform systems supported by a central file server, it's common to store the build directory in a location specific to the platform and to the version of lcc, and to point a symbolic link to this location. For example,

    % ln -s /usr/local/lib/lcc-4.1/sparc-solaris /usr/local/lib/lcc

    points /usr/local/lib/lcc to a build directory for lcc version 4.1 on the SPARC under Solaris. Links into /usr/local/lib are created for the programs lcc and bprint. Thus, a new distribution can be installed by building it in its own build directory and changing one symbolic link to point to that directory. If these conventions or their equivalents are followed, the host-specific parts of the driver program, lcc, can be used unmodified.

    Installation on a UNIX system involves the following steps. Below, the build directory is referred to as BUILDDIR.

    1. Create the build directory, using a version- and platform-specific naming convention as suggested above, and record the name of this directory in the BUILDDIR environment variable:
      % setenv BUILDDIR /usr/local/lib/lcc-4.1/sparc-solaris
      % mkdir -p $BUILDDIR

      Here and below, commands assume the C shell. Also, you'll need a version of mkdir that supports the -p option, which creates intermediate directories as necessary.

    2. Copy the man pages to the repository for local man pages, e.g.,
      % cp doc/*.1 /usr/local/man/man1

      Some users copy the man pages to the build directory and create the appropriate symbolic links, e.g.,

      % cp doc/*.1 $BUILDDIR
      % ln -s $BUILDDIR/*.1 /usr/local/man/man1
    3. Platform-specific include files are in directories named include/target/os. Create the include directory in the build directory, and copy the include hierarchy for your platform to this directory, e.g.,
      % mkdir $BUILDDIR/include
      % cp -p -R include/sparc/solaris/* $BUILDDIR/include

      Again, some users create a symbolic link to the appropriate directory in the distribution instead of copying the include files. For example, at Princeton, the distributions are stored under /proj/pkg/lcc, so the included files are "installed" by creating one symbolic link:

      % ln -s /proj/pkg/lcc/4.1/include/sparc/solaris $BUILDDIR/include

      If you're installing lcc on Linux, you must also plant a symbolic link named gcc to gcc's library directory, because lcc uses gcc's C preprocessor and most of gcc's header files:

      % ln -s /usr/lib/gcc-lib/i486-linux/2.7.2.2 $BUILDDIR/gcc

      The library directory shown above may be different on your Linux machine; to determine the correct directory, browse /usr/lib/gcc-lib, or execute

      % cc -v tst/8q.c

      and examine the diagnostic output. Make sure that $BUILDDIR/gcc/cpp and $BUILDDIR/gcc/include point to, respectively, gcc's C preprocessor and header files. On Linux, lcc looks for include files in $BUILDDIR/include, $BUILDDIR/gcc/include, and /usr/include, in that order; see Building the Driver and etc/linux.c for details.

    4. The makefile includes the file named by the CUSTOM macro; the default is custom.mk, and an empty custom.mk is included in the distribution. If desired, prepare a site-specification customization file and define CUSTOM to the path of that file when invoking make in steps 5 and 6, e.g.,
      make CUSTOM=/users/drh/solaris.mk

      You can, for example, use customization files to record site-specific values for macros instead of using environment variables, and to record targets for the steps in this list.

    5. Build the host-specific driver, creating a custom host-specific part, if necessary. See Building the Driver.
    6. Build the preprocessor, compiler proper, library, and other accessories. See Building the Compiler.
    7. Plant symbolic links to the build directory and to the installed programs, e.g.,
      % ln -s $BUILDDIR /usr/local/lib/lcc
      % ln -s /usr/local/lib/{lcc,bprint} /usr/local/bin

      Some users copy bprint and lcc into /usr/local/bin instead of creating symbolic links. The advantange of creating the links for lcc and bprint as shown is that, once established, they point indirectly to whatever /usr/local/lib/lcc points to; installing a new version of lcc, say, 4.2, can be done by changing /usr/local/lib/lcc to point to the 4.2 build directory.

    Building the Driver

    The preprocessor, compiler, assembler, and loader are invoked by a driver program, lcc, which is similar to cc on most systems. It's described in the man page doc/lcc.1. The driver is built by combining the host-independent part, etc/lcc.c, with a small host-specific part. Distributed host-specific parts are named etc/os.c, where os is the name of the operating system for the host on which lcc is being installed. If you're following the installations conventions described above, you can probably use one of the host-specific parts unmodified; otherwise, pick one that is closely related to your platform, copy it to whatever.c, and edit it as described below. You should not have to edit etc/lcc.c.

    We'll use etc/solaris.c as an example in describing how the host-specific part works. This example illustrates all the important features. Make sure you have the environment variable BUILDDIR set correctly, and build the driver with a make command, e.g.,

    % make HOSTFILE=etc/solaris.c lcc
    cc -g -c -DTEMPDIR=\"/tmp\" -o /usr/local/lib/lcc-4.1/sparc-solaris/lcc.o etc/lcc.c
    cc -g -c -o /usr/local/lib/lcc-4.1/sparc-solaris/host.o etc/solaris.c
    cc -g -o /usr/local/lib/lcc-4.1/sparc-solaris/lcc /usr/local/lib/lcc-4.1/sparc-solaris/lcc.o /usr/local/lib/lcc-4.1/sparc-solaris/host.o

    The symbolic name HOSTFILE specifies the path to the host-specific part, either one in the distribution or whatever.c. Some versions of make may require the -e option in order to read the environment.

    Here's etc/solaris.c:

    /* Sparcs running Solaris 2.5.1 at CS Dept., Princeton University */
    
    #include <string.h>
    
    static char rcsid[] = "$ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $";
    
    #ifndef LCCDIR
    #define LCCDIR "/usr/local/lib/lcc/"
    #endif
    #ifndef SUNDIR
    #define SUNDIR "/opt/SUNWspro/SC4.2/lib/"
    #endif
    
    char *suffixes[] = { ".c", ".i", ".s", ".o", ".out", 0 };
    char inputs[256] = "";
    char *cpp[] = { LCCDIR "cpp",
    	"-D__STDC__=1", "-Dsparc", "-D__sparc__", "-Dsun", "-D__sun__", "-Dunix",
    	"$1", "$2", "$3", 0 };
    char *include[] = { "-I" LCCDIR "include", "-I/usr/local/include",
    	"-I/usr/include", 0 };
    char *com[] = { LCCDIR "rcc", "-target=sparc/solaris",
    	"$1", "$2", "$3", 0 };
    char *as[] = { "/usr/ccs/bin/as", "-Qy", "-s", "-o", "$3", "$1", "$2", 0 };
    char *ld[] = { "/usr/ccs/bin/ld", "-o", "$3", "$1",
    	SUNDIR "crti.o", SUNDIR "crt1.o",
    	SUNDIR "values-xa.o", "$2", "",
    	"-Y", "P," SUNDIR ":/usr/ccs/lib:/usr/lib", "-Qy",
    	"-L" LCCDIR, "-llcc", "-lm", "-lc", SUNDIR "crtn.o", 0 };
    
    extern char *concat(char *, char *);
    
    int option(char *arg) {
    	if (strncmp(arg, "-lccdir=", 8) == 0) {
    		cpp[0] = concat(&arg[8], "/cpp");
    		include[0] = concat("-I", concat(&arg[8], "/include"));
    		ld[12] = concat("-L", &arg[8]);
    		com[0] = concat(&arg[8], "/rcc");
    	} else if (strcmp(arg, "-p") == 0) {
    		ld[5] = SUNDIR "mcrt1.o";
    		ld[10] = "P," SUNDIR "libp:/usr/ccs/lib/libp:/usr/lib/libp:"
    			 SUNDIR ":/usr/ccs/lib:/usr/lib";
    	} else if (strcmp(arg, "-b") == 0)
    		;
    	else if (strncmp(arg, "-ld=", 4) == 0)
    		ld[0] = &arg[4];
    	else
    		return 0;
    	return 1;
    }

    LCCDIR defaults to "/usr/local/lib/lcc/" unless it's defined by a -D option as part of CFLAGS in the make command, e.g.,

    % make HOSTFILE=etc/solaris.c CFLAGS='-DLCCDIR=\"/v/lib/lcc/\"' lcc

    Note the trailing slash; SUNDIR is provided so you can use etc/solaris.c even if you have a different version of the Sun Pro compiler suite. If you're using the gcc compiler tools instead of the Sun Pro tools, see etc/gcc-solaris.c.

    Most of the host-specific code is platform-specific data and templates for the commands that invoke the preprocessor, compiler, assembler, and loader. The suffixes array lists the file name suffixes for C source files, preprocessed source files, assembly language source files, object files, and executable files. suffixes must be terminated with a null pointer, as shown above. The initialization of suffixes in etc/solaris.c are the typical ones for UNIX systems. Each element of suffixes is actually a list of suffixes, separated by semicolons; etc/win32.c holds an example:

    char *suffixes[] = { ".c;.C", ".i;.I", ".asm;.ASM;.s;.S", ".obj;.OBJ", ".exe", 0 };

    When a list is given, the first suffix is used whenever lcc needs to generate a file name. For example, with etc/win32.c, lcc emits the generated assembly code into .asm files.

    The inputs array holds a null-terminated string of directories separated by colons or semicolons. These are used as the default value of LCCINPUTS, if the environment variable LCCINPUTS is not set; see the man page.

    Each command template is an array of pointers to strings terminated with a null pointer; the strings are full path names of commands, arguments, or argument placeholders, which are described below. Commands are executed in a child process, and templates can contain multiple commands by separating commands with newlines. The driver runs each command in a new process.

    The cpp array gives the command for running lcc's preprocessor, cpp. Literal arguments specified in templates, e.g., "-Dsparc" in the cpp command above, are passed to the command as given.

    The strings "$1", "$2", and "$3" in templates are placeholders for lists of arguments that are substituted in a copy of the template before the command is executed. $1 is replaced by the options specified by the user; for the preprocessor, this list always contains at least -D__LCC__. $2 is replaced by the input files, and $3 is replaced by the output file.

    Zero-length arguments after replacement are removed from the argument list before the command is invoked. So, for example, if the preprocessor is invoked without an output file, "$3" becomes "", which is removed from the final argument list.

    The include array is a list of -I options that specify which directives should be searched to satisfy include directives. These directories are searched in the order given. The first directory should be the one to which the ANSI header files were copied as described in UNIX or Windows installation instructions. The driver adds these options to cpp's arguments when it invokes the preprocessor, except when -N is specified.

    com gives the command for invoking the compiler. This template can appear as shown above in a custom host-specific part, but the option -target=sparc/solaris should be edited to the target/os for your platform. If com[1] includes the string "win32", the driver assumes it's running on Windows. lcc can generate code for all of the target/os combinations listed in the file src/bind.c. The -target option specifies the default combination. The driver's -Wf option can be used to specify other combinations; the man page elaborates.

    as gives the command for invoking the assembler. On Linux, you must be running at least version 2.8.1 of the GNU assembler; earlier versions mis-assemble some instructions emitted by lcc.

    ld gives the command for invoking the loader. For the other commands, the list $2 contains a single file; for ld, $2 contains all ".o" files and libraries, and $3 is a.out, unless the -o option is specified. As suggested in the code above, ld must also specify the appropriate startup code and default libraries, including the lcc library, liblcc.a.

    The option function is described below; the minimal option function just returns 0.

    You can test lcc with the options -v -v to display the commands that would be executed, e.g.,

    % $BUILDDIR/lcc -v -v foo.c baz.c mylib.a -lX11
    /usr/local/lib/lcc-4.1/lcc $ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $
    foo.c:
    /usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__ -I/usr/local/lib/lcc/include -I/usr/local/include -I/usr/include foo.c /tmp/lcc266290.i
    /usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc266290.i /tmp/lcc266291.
    s
    /usr/ccs/bin/as -Qy -s -o /tmp/lcc266292.o /tmp/lcc266291.s
    baz.c:
    /usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__ -I/usr/local/lib/lcc/include -I/usr/local/include -I/usr/include baz.c /tmp/lcc266290.i
    /usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc266290.i /tmp/lcc266291.s
    /usr/ccs/bin/as -Qy -s -o /tmp/lcc266293.o /tmp/lcc266291.s
    /usr/ccs/bin/ld -o a.out /opt/SUNWspro/SC4.2/lib/crti.o /opt/SUNWspro/SC4.2/lib/crt1.o /opt/SUNWspro/SC4.2/lib/values-xa.o /tmp/lcc266292.o /tmp/lcc266293.o mylib.a -lX11 -Y P,/opt/SUNWspro/SC4.2/lib/:/usr/ccs/lib:/usr/lib -Qy -L/usr/local/lib/lcc/ -llcc -lm -lc /opt/SUNWspro/SC4.2/lib/crtn.o
    rm /tmp/lcc266293.o /tmp/lcc266290.i /tmp/lcc266291.s /tmp/lcc266292.o

    As the output shows, lcc places temporary files in /tmp; if any of the environment variables TMP, TEMP, and TMPDIR are set, they override this default (in the order shown) as does the -tempdir=dir option. The default can be changed by defining TEMPDIR in CFLAGS when building the driver.

    The option function is called for the options -Wo, -g, -p, -pg, and -b because these compiler options might also affect the loader's arguments. For these options, the driver calls option(arg) to give the host-specific code an opportunity to edit the ld command, if necessary. option can change ld, if necessary, and return 1 to announce its acceptance of the option. If the option is unsupported, option should return 0.

    For example, in response to -g, the option function shown above accepts the option but does nothing else, because the ld and as commands don't need to be modified on the SPARC. -g will also be added to the compiler's options by the host-independent part of the driver. The -p causes option to change the name of the startup code and changed the list of libraries. The -b option turns on lcc's per-expression profiling, the code for which is in liblcc.a, so option need no nothing.

    On SPARCs, the driver also recognizes -Bstatic and -Bdynamic as linker options. The driver recognizes but ignores "-target name" option.

    The option -Woarg causes the driver to pass arg to option. Such options have no other effect; this mechanism is provided to support system-specific options that affect the commands executed by the driver. As illustrated above, host-specific parts should support the -Wo-lccdir=dir option, which causes lcc's compilation components to be found in dir, because this option is used by the test scripts, and because the driver simulates a -Wo-lccdir option with the value of the environment variable LCCDIR, if it's defined. The code above rebuilds the paths to the include files, preprocessor, compiler, and library by calling concat, which is defined in etc/lcc.c.

    Building the Compiler and Accessories

    To build the rest of compilation components make sure BUILDDIR is set appropriately and type "make all". This command builds librcc.a (the compiler's private library), rcc (the compiler proper), lburg (the code-generator generator), cpp (the preprocessor), liblcc.a (the runtime library), and bprint (the profile printer), all in BUILDDIR. There may be warnings, but there should be no errors. If you're using an ANSI/ISO compiler other than cc, specify its name with the CC= option, e.g., "make CC=gcc all". If you're running on a DEC ALPHA, use "make CC='cc -std1' all"; the -std1 option is essential on the ALPHA. If you're on a DEC 5000 running Ultrix 4.3, use "make CC=c89 all".

    Once rcc is built with the host C compiler, run the test suite to verify that rcc is working correctly. If any of the steps below fail, contact us (see Reporting Bugs). The commands in the makefile run the shell script src/run.sh on each C program in the test suite, tst/*.c. It uses the driver, $BUILDDIR/lcc, so you must have the driver in the build directory before testing rcc. The target/os combination is read from the variable TARGET, which must be specified when invoking make:

    % make TARGET=sparc/solaris test
    mkdir -p /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/8q.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/array.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cf.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cq.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/cvt.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/fields.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/front.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/incr.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/init.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/limits.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/paranoia.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/sort.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/spill.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/stdarg.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/struct.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/switch.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/wf1.s:
    /usr/local/lib/lcc-4.1/sparc-solaris/rcc -target=sparc/solaris /usr/local/lib/lcc-4.1/sparc-solaris/sparc/solaris/tst/yacc.s:

    Each line in the output above is of the form

    $BUILDDIR/rcc -target=target/os$BUILDDIR/target/os/X.s:

    where X is the base name of the C program X.c in the test suite. This output identifies the compiler and the target, e.g., "$BUILDDIR/rcc is generating code for a sparc running the solaris operating system."

    For each program in the test suite, src/run.sh compiles the program, drops the generated assembly language code in BUILDDIR/target/os, and uses diff to compare the generated assembly code with the expected code (the code expected for tst/8q.c on the SPARC under Solaris is in sparc/solaris/tst/8q.sbk, etc.). If there are differences, the script executes the generated code with the input given in tst (the input for tst/8q.c is in tst/8q.0, etc.) and compares the output with the expected output (the expected output from tst/8q.c on the SPARC under Solaris is in sparc/solaris/tst/8q.1bk, etc.). The script also compares the diagnostics from the compiler with the expected diagnostics.

    On some systems, there may be a few differences between the generated code and the expected code. These differences occur because the expected code is generated by cross compilation and the least significant bits of some floating-point constants differ from those bits in constants generated on your system. On Linux, there may be differences because of differences in the header files between our system and yours. There should be no differences in the output from executing the test programs.

    Next, run the "triple test", which builds rcc using itself:

    % make triple
    /usr/local/lib/lcc-4.1/sparc-solaris/lcc -o /usr/local/lib/lcc-4.1/sparc-solaris/1rcc -d0.6 -Wo-lccdir=/usr/local/lib/lcc-4.1/sparc-solaris -B/usr/local/lib/lcc-4.1/sparc-solaris/  -Isrc src/*.c
    src/alloc.c:
    ...
    src/x86.c:
    /usr/local/lib/lcc-4.1/sparc-solaris/lcc -o /usr/local/lib/lcc-4.1/sparc-solaris/1rcc -d0.6 -Wo-lccdir=/usr/local/lib/lcc-4.1/sparc-solaris -B/usr/local/lib/lcc-4.1/sparc-solaris/  -Isrc src/*.c
    src/alloc.c:
    ...
    src/x86.c:
    strip /usr/local/lib/lcc-4.1/sparc-solaris/[12]rcc
    dd if=/usr/local/lib/lcc-4.1/sparc-solaris/1rcc of=/usr/local/lib/lcc-4.1/sparc-solaris/rcc1 bs=512 skip=1
    769+1 records in
    769+1 records out
    dd if=/usr/local/lib/lcc-4.1/sparc-solaris/2rcc of=/usr/local/lib/lcc-4.1/sparc-solaris/rcc2 bs=512 skip=1
    769+1 records in
    769+1 records out
    if cmp /usr/local/lib/lcc-4.1/sparc-solaris/rcc[12]; then \
            mv /usr/local/lib/lcc-4.1/sparc-solaris/2rcc /usr/local/lib/lcc-4.1/sparc-solaris/rcc; \
            rm -f /usr/local/lib/lcc-4.1/sparc-solaris/1rcc /usr/local/lib/lcc-4.1/sparc-solaris/rcc[12]; fi

    This command builds rcc twice; once using the rcc built by cc and again using the rcc built by lcc. The resulting binaries are compared. They should be identical, as shown at the end of the output above. If they aren't, our compiler is generating incorrect code; contact us.

    The final version of rcc should also pass the test suite; that is, the output from

    % make TARGET=sparc/solaris test

    should be identical to that from the previous make test.

    The command "make clean" cleans up, but does not remove rcc, etc., and "make clobber" cleans up and removes lcc, rcc, and the other accessories. Test directories under BUILDDIR are not removed; you'll need to remove these by hand, e.g.,

    % rm -fr $BUILDDIR/sparc

    The code generators for the other targets can be tested by specifying the desired target/os and setting an environment variable that controls what src/run.sh does. For example, to test the MIPS code generator, type

    % setenv REMOTEHOST noexecute
    % make TARGET=mips/irix test

    As above, src/run.sh compares the MIPS code generated with what's expected. There should be no differences. Setting REMOTEHOST to noexecute suppresses the assembly and execution of the generated code. If you set REMOTEHOST to the name of a MIPS machine to which you can rlogin, src/run.sh will rcp the generated code to that machine and execute it there, if necessary. See src/run.sh for the details.

    You can use lcc as a cross compiler. The options -S and -Wf-target=target/os generate assembly code for the specified target, which is any of those listed in the file src/bind.c. For example,

    % lcc -Wf-target=mips/irix -S tst/8q.c

    generates MIPS code for tst/8q.c in 8q.s.

    lcc can also generate code for a "symbolic" target. This target is used routinely in front-end development, and its output is a printable representation of the input program, e.g., the dags constructed by the front end are printed, and other interface functions print their arguments. You can specify this target with the option -Wf-target=symbolic. For example,

    % lcc -Wf-target=symbolic -S tst/8q.c

    generates symbolic output for tst/8q.c in 8q.s. Adding -Wf-html causes the symbolic target to emit HTML instead of plain text. Finally, the option -Wf-target=null specifies the "null" target for which lcc emits nothing and thus only checks the syntax and semantics of its input files.

    Installation on Windows NT 4.0 or Windows 95/98

    On Windows NT 4.0 and Windows 95/98, lcc is designed to work with Microsoft's Visual C++ 5.0 (VC) and Microsoft's Assembler, MASM 6.11d. It uses the VC header files, libraries, and command-line tools, and it uses MASM to assemble the code it generates. If you have MASM 6.11, make sure you upgrade to 6.11d, because earlier 6.11 releases do not generate correct COFF object files.

    Building the distribution components from the ground up requires Microsoft's Visual C/C++ 5.0 compiler, Microsoft's make, nmake, and the standard Windows command interpreter. makefile.nt is written to use only nmake. As on UNIX systems, the compilation components are installed in a single build directory, and the top-level programs, lcc.exe and bprint.exe, are installed in a directory on the PATH. If the conventions used below are followed, the Windows-specific parts of the driver program, lcc.exe, can be used unmodified.

    Building from the source distribution on a Windows system involves the following steps. Below, the build directory is referred to as BUILDDIR, and the distribution is in \dist\lcc\4.1.

    1. Create the build directory, perhaps using a version- and platform-specific naming convention as suggested in Installation on UNIX, and record the name of this directory in the BUILDDIR environment variable:
      C:\dist\lcc\4.1>set BUILDDIR=\progra~1\lcc\4.1\bin
      C:\dist\lcc\4.1>mkdir %BUILDDIR%

      The default build, or installation, directory is \Program Files\lcc\4.1\bin, but the nmake commands require that you use the corresponding 8.3 file name, progra~1, instead of Program Files.

    2. etc\win32.c is the Windows-specific part of the driver. It assumes that environment variable include gives the locations of the VC header files and that the linker (link.exe) and the assembler (ml.exe) are on the PATH. It also assumes that the macro LCCDIR gives the build directory. If necessary, revise a copy of etc\win32.c to reflect the conventions on your computer (see Building the Driver), then build the driver, specifying the default temporary directory, if necessary:
      C:\dist\lcc\4.1>nmake -f makefile.nt TEMPDIR=\\temp HOSTFILE=etc/win32.c lcc
      ...
              cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -c -DTEMPDIR=\"\\temp\" -Fo\progra~1\lcc\4.1\bin\lcc.obj etc/lcc.c
      lcc.c
              cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -c -Fo\progra~1\lcc\4.1\bin\host.obj etc/win32.c
      win32.c
              cl -nologo -Zi -MLd -Fd\progra~1\lcc\4.1\bin\ -Fe\progra~1\lcc\4.1\bin\lcc.exe \progra~1\lcc\4.1\bin\lcc.obj \progra~1\lcc\4.1\bin\host.obj

      If you make a copy of etc\win32.c, specify the path of the copy as the value of HOSTFILE. For example, if you copy etc\win32.c to BUILDDIR and edit it, use the command

      C:\dist\lcc\4.1>nmake -f makefile.nt TEMPDIR=\\temp HOSTFILE=%BUILDDIR%\win32.c lcc
    3. Build the preprocessor, compiler proper, library, and other accessories (see Building the Compiler):
      C:\dist\lcc\4.1>nmake -f makefile.nt all

      This command uses the VC command-line tools cl and lib to build bprint.exe, cpp.exe, lburg.exe, liblcc.lib, librcc.lib, and rcc.exe, all in BUILDDIR. There may be some warnings, but there should be no warnings.

    4. Create a test directory and run the test suite:
      C:\dist\lcc\4.1>mkdir %BUILDDIR%\x86\win32\tst
      C:\dist\lcc\4.1>nmake -f makefile.nt test

      This command compiles each program in tst, compares the generated assembly code and diagnostics with the expected assembly code and diagnostics, executes the program, and compares the output with the expected output (using fc). For example, when the nmake command compiles tst\8q.c, it leaves the generated assembly code and diagnostic output in %BUILDDIR%\x86\win32\tst\8q.s and %BUILDDIR%\x86\win32\tst\8q.2, and it compares them with the expected results in x86\win32\tst\8q.sbk. It builds the executable program in %BUILDDIR%\x86\win32\tst\8q.exe, runs it, and redirects the output to %BUILDDIR%\x86\win32\tst\8q.1, which it compares with x86\win32\tst\8q.1bk. The output from this step is voluminous, but there should be no differences and no errors.

    5. Run the "triple" test, which compiles rcc with itself and verifies the results:
      C:\dist\lcc\4.1>nmake -f makefile.nt triple
      ...
      \progra~1\lcc\4.1\bin\x86.c:
       Assembling: C:/TEMP/lcc2001.asm
              fc /b \progra~1\lcc\4.1\bin\1rcc.exe \progra~1\lcc\4.1\bin\2rcc.exe
      Comparing files \progra~1\lcc\4.1\bin\1rcc.exe and \progra~1\lcc\4.1\bin\2RCC.EXE
      00000088: B4 D5

      This command builds rcc twice; once using the rcc built by VC and again using the rcc built by lcc. The resulting binaries are compared using fc. They should be identical, except for one or two bytes of timestamp data, as shown at the end of the output above. If they aren't, our compiler is generating incorrect code; contact us.

    6. Copy lcc.exe and bprint.exe to a directory on your PATH, e.g.,
      C:\dist\lcc\4.1>copy %BUILDDIR%\lcc.exe \bin
              1 file(s) copied.
      
      C:\dist\lcc\4.1>copy %BUILDDIR%\bprint.exe \bin
              1 file(s) copied.
    7. Finally, clean up:
      C:\dist\lcc\4.1>nmake -f makefile.nt clean

      This command removes the derived files in BUILDDIR, but does not remove rcc.exe, etc.; "nmake -f makefile.nt clobber" cleans up and removes all executables and libraries. Test directories under BUILDDIR are not removed; you'll need to remove these by hand, e.g.,

      C:\dist\lcc\4.1>rmdir %BUILDDIR%\x86 /s
      \progra~1\lcc\4.1\bin\x86, Are you sure (Y/N)? y

    Reporting Bugs

    lcc is a large, complex program. We find and repair errors routinely. If you think that you've found a error, follow the steps below, which are adapted from the instructions in Chapter 1 of A Retargetable C Compiler: Design and Implementation.

    1. If you don't have a source file that displays the error, create one. Most errors are exposed when programmers try to compile a program they think is valid, so you probably have a demonstration program already.
    2. Preprocess the source file and capture the preprocessor output. Discard the original code.
    3. Prune your source code until it can be pruned no more without sending the error into hiding. We prune most error demonstrations to fewer than five lines.
    4. Confirm that the source file displays the error with the distributed version of lcc. If you've changed lcc and the error appears only in your version, then you'll have to chase the error yourself, even if it turns out to be our fault, because we can't work on your code.
    5. Annotate your code with comments that explain why you think that lcc is wrong. If lcc dies with an assertion failure, please tell us where it died. If lcc crashes, please report the last part of the call chain if you can. If lcc is rejecting a program you think is valid, please tell us why you think it's valid, and include supporting page numbers in the ANSI Standard, Appendix A in The C Programming Language, or the appropriate section in C: A Reference Manual, 4th edition by S. B. Harbison and G. L. Steele, Jr. (Prentice Hall, 1995). If lcc silently generates incorrect code for some construct, please include the corrupt assembly code in the comments and flag the incorrect instructions if you can.
    6. Confirm that your error hasn't been fixed already. The latest version of lcc is always available for anonymous ftp from ftp.cs.princeton.edu in pub/lcc. A README file there gives acquistion details, and the LOG file reports what errors were fixed and when they were fixed. If you report a error that's been fixed, you might get a canned reply.
    7. Send your program by electronic mail to lcc-bugs@cs.princeton.edu. Please send only valid C programs; put all remarks in C comments so that we can process reports semiautomatically.

    Keeping in Touch

    There is an lcc mailing list for general information about lcc. To be added to the list, send a message with the 1-line body

    subscribe lcc

    to majordomo@cs.princeton.edu. This line must appear in the message body; "Subject:" lines are ignored. To learn more about mailing lists served by majordomo, send a message with the 1-word body "help" to majordomo@cs.princeton.edu. Mail sent to lcc@cs.princeton.edu is forwarded to everyone on the mailing list.

    There is also an lcc-bugs mailing list for reporting bugs; subscribe to it by sending a message with the 1-line body

    subscribe lcc-bugs

    to majordomo@cs.princeton.edu. Mail addressed to lcc-bugs@cs.princeton.edu is forwarded to everyone on this list.


    Chris Fraser / cwfraser@microsoft.com
    David Hanson / drh@microsoft.com
    $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $
    openarena_0.8.8.orig/code/tools/lcc/doc/4.html0000644000175000017500000005663711656310262017656 0ustar smcvsmcv The lcc 4.1 Code-Generation Interface

    The lcc 4.1 Code-Generation Interface

    Christopher W. Fraser and David R. Hanson, Microsoft Research

    Contents

  • Introduction
  • 5.1 Type Metrics
  • 5.3 Symbols
  • 5.5 Dag Operators
  • 5.6 Interface Flags
  • 5.8 Definitions
  • 5.9 Constants
  • 5.12 Upcalls
  • Introduction

    Version 4.1 is the latest release of lcc, the ANSI C compiler described in our book A Retargetable C Compiler: Design and Implementation (Addison-Wesley, 1995, ISBN 0-8053-1670-1). This document summarizes the differences between the 4.1 code-generation interface and the 3.x interface described in Chap. 5 of A Retargetable C Compiler.

    Previous versions of lcc supported only three sizes of integers, two sizes of floats, and insisted that pointers fit in unsigned integers (see Sec. 5.1 of A Retargetable C Compiler). These assumptions simplified the compiler, and were suitable for 32-bit architectures. But on 64-bit architectures, such as the DEC ALPHA, it's natural to have four sizes of integers and perhaps three sizes of floats, and on 16-bit architectures, 32-bit pointers don't fit in unsigned integers. Also, the 3.x constaints limited the use of lcc's back ends for other languages, such as Java.

    Version 4.x removes all of these restrictions: It supports any number of sizes for integers and floats, and the size of pointers need not be related to the size of any of the integer types. The major changes in the code-generation interface are:

    • The number of type suffixes has been reduced to 6.
    • Dag operators are composed of a generic operator, a type suffix, and a size.
    • Unsigned variants of several operators have been added.
    • Several interface functions have new signatures.

    In addition, version 4.x is written in ANSI C and uses the standard I/O library and other standard C functions.

    The sections below parallel the subsections of Chap. 5 of A Retargetable C Compiler and summarize the differences between the 3.x and 4.x code-generation interface. Unaffected subsections are omitted. Page citations refer to pages in A Retargetable C Compiler.

    5.1 Type Metrics

    There are now 10 metrics in an interface record:

    Metrics charmetric;
    Metrics shortmetric;
    Metrics intmetric;
    Metrics longmetric;
    Metrics longlongmetric;
    Metrics floatmetric;
    Metrics doublemetric;
    Metrics longdoublemetric;
    Metrics ptrmetric;
    Metrics structmetric;

    Each of these specifies the size and alignment of the corresponding type. ptrmetric describes all pointers.

    5.3 Symbols

    The actual value of a constant is stored in the u.c.v field of a symbol, which holds a Value:

    typedef union value {
    	long i;
    	unsigned long u;
    	long double d;
    	void *p;
    	void (*g)(void);
    } Value;

    The value is stored in the appropriate field according to its type, which is given by the symbol's type field.

    5.5 Dag Operators

    The op field a of node structure holds a dag operator, which consists of a generic operator, a type suffix, and a size indicator. The type suffixes are:

    enum {
    	F=FLOAT,
    	I=INT,
    	U=UNSIGNED,
    	P=POINTER,
    	V=VOID,
    	B=STRUCT
    };
    
    #define sizeop(n) ((n)<<10)

    Given a generic operator o, a type suffix t, and a size s, a type- and size-specific operator is formed by o+t+sizeop(s). For example, ADD+F+sizeop(4) forms the operator ADDF4, which denotes the sum of two 4-byte floats. Similarly, ADD+F+sizeop(8) forms ADDF8, which denotes 8-byte floating addition. In the 3.x code-generation interface, ADDF and ADDD denoted these operations. There was no size indicator in the 3.x operators because the type suffix supplied both a type and a size.

    Table 5.1 lists each generic operator, its valid type suffixes, and the number of kids and syms that it uses; multiple values for kids indicate type-specific variants. The notations in the syms column give the number of syms values and a one-letter code that suggests their uses: 1V indicates that syms[0] points to a symbol for a variable, 1C indicates that syms[0] is a constant, and 1L indicates that syms[0] is a label. For 1S, syms[0] is a constant whose value is a size in bytes; 2S adds syms[1], which is a constant whose value is an alignment. For most operators, the type suffix and size indicator denote the type and size of operation to perform and the type and size of the result.

    Table 5.1|Node Operators.
    syms kids Operator Type Suffixes Sizes Operation
    1V 0 ADDRF ...P.. p address of a parameter
    1V 0 ADDRG ...P.. p address of a global
    1V 0 ADDRL ...P.. p address of a local
    1C 0 CNST FIUP.. fdx csilh p constant
    |
    1 BCOM .IU... ilh bitwise complement
    1S 1 CVF FI.... fdx ilh convert from float
    1S 1 CVI FIU... fdx csilh csilhp convert from signed integer
    1S 1 CVP ..U.. p convert from pointer
    1S 1 CVU .IUP.. csilh p convert from unsigned integer
    1 INDIR FIUP.B fdx csilh p fetch
    1 NEG FI.... fdx ilh negation
    |
    2 ADD FIUP.. fdx ilh ilhp p addition
    2 BAND .IU... ilh bitwise AND
    2 BOR .IU... ilh bitwise inclusive OR
    2 BXOR .IU... ilh bitwise exclusive OR
    2 DIV FIU... fdx ilh division
    2 LSH .IU... ilh left shift
    2 MOD .IU... ilh modulus
    2 MUL FIU... fdx ilh multiplication
    2 RSH .IU... ilh right shift
    2 SUB FIUP.. fdx ilh ilhp p subtraction
    |
    2S 2 ASGN FIUP.B fdx csilh p assignment
    1L 2 EQ FIU... fdx ilh ilhp jump if equal
    1L 2 GE FIU... fdx ilh ilhp jump if greater than or equal
    1L 2 GT FIU... fdx ilh ilhp jump if greater than
    1L 2 LE FIU... fdx ilh ilhp jump if less than or equal
    1L 2 LT FIU... fdx ilh ilhp jump if less than
    1L 2 NE FIU... fdx ilh ilhp jump if not equal
    2S 1 ARG FIUP.B fdx ilh p argument
    1 1 or 2 CALL FIUPVB fdx ilh p function call
    1 RET FIUPV. fdx ilh p return from function
    |
    1 JUMP ....V. unconditional jump
    1L 0 LABEL ....V. label definition

    The entries in the Sizes column indicate sizes of the operators that back ends must implement. Letters denote the size of float (f), double (d), long double (x), character (c), short integer (s), integer (i), long integer (l), "long long" integer (h) , and pointer (p). These sizes are separated into sets for each type suffix, except that a single set is used for both I and U when the set for I is identical to the set for U.

    The actual values for the size indicators, fdxcsilhp, depend on the target. A specification like ADDFf denotes the operator ADD+F+sizeop(f), where "f" is replaced by a target-dependent value, e.g., ADDF4 and ADDF8. For example, back ends must implement the following CVI and MUL operators.

    CVIFf CVIFd CVIFx
    CVIIc CVIIs CVIIi CVIIl CVIIh
    CVIUc CVIUs CVIUi CVIUl CVIUh CVIUp

    MULFf MULFd MULFx
    MULIi MULIl MULIh
    MULUi MULUl MULUh

    On most platforms, there are fewer than three sizes of floats and six sizes of integers, and pointers are usually the same size as one of the integers. And lcc doesn't support the "long long" type, so h is not currently used. So the set of platform-specific operators is usually smaller than the list above suggests. For example, the X86, SPARC, and MIPS back ends implement the following CVI and MUL operators.

    CVIF4 CVIF8
    CVII1 CVII2 CVII4
    CVIU1 CVIU2 CVIU4

    MULF4 MULF8
    MULI4
    MULU4

    The set of operators is thus target-dependent; for example, ADDI8 appears only if the target supports an 8-byte integer type. ops.c is a program that, given a set of sizes, prints the required operators and their values, e.g.,

    % ops c=1 s=2 i=4 l=4 h=4 f=4 d=8 x=8 p=4
    ...
     CVIF4=4225 CVIF8=8321
     CVII1=1157 CVII2=2181 CVII4=4229
     CVIU1=1158 CVIU2=2182 CVIU4=4230
    ...
     MULF4=4561 MULF8=8657
     MULI4=4565
     MULU4=4566
    ...
    131 operators

    The type suffix for a conversion operator denotes the type of the result and the size indicator gives the size of the result. For example, CVUI4 converts an unsigned (U) to a 4-byte signed integer (I4). The syms[0] field points to a symbol-table entry for a integer constant that gives the size of the source operand. For example, if syms[0] in a CVUI4 points to a symbol-table entry for 2, the conversion widens a 2-byte unsigned integer to a 4-byte signed integer. Conversions that widen unsigned integers zero-extend; those that widen signed integers sign-extend.

    The front end composes conversions between types T1 and T2 by widening T1 to it's "supertype", if necessary, converting that result to T2's supertype, then narrowing the result to T2, if necessary. The following table lists the supertypes; omitted entries are their own supertypes.

    Type | Supertype
    signed char int
    signed short int
    unsigned char int, if sizeof (char) < sizeof (int)
    unsigned, otherwise
    unsigned short int, if sizeof (short) < sizeof (int)
    unsigned, otherwise
    void * an unsigned type as large as a pointer

    Pointers are converted to an unsigned type of the same size, even when that type is not one of the integer types.

    For example, the front end converts a signed short to a float by first converting it to an int and then to a float. It converts an unsigned short to an int with a single CVUIi conversion, when shorts are smaller than ints.

    There are now signed and unsigned variants of ASGN, INDIR, BCOM, BOR, BXOR, BAND, ARG, CALL, and RET to simplify code generation on platforms that use different instructions or register set for signed and unsigned operations. Likewise there are now pointer variants of ASGN, INDIR, ARG, CALL, and RET.

    5.6 Interface Flags

    unsigned unsigned_char:1;

    tells the front end whether plain characters are signed or unsigned. If it's zero, char is a signed type; otherwise, char is an unsigned type.

    All the interface flags can be set by command-line options, e.g., -Wf-unsigned_char=1 causes plain characters to be unsigned.

    5.8 Definitions

    The front end announces local variables by calling

    void (*local)(Symbol);

    It announces temporaries likewise; these have the symbol's temporary flag set, which indicates that the symbol will be used only in the next call to gen. If a temporary's u.t.cse field is nonnull, it points to the node that computes the value assigned to the temporary; see page 346.

    The front end calls

    void (*address)(Symbol p, Symbol q, long n);

    to initialize q to a symbol that represents an address of the form x+n, where x is the address represented by p and the long integer n is positive or negative.

    5.9 Constants

    The interface function

    void (*defconst)(int suffix, int size, Value v);

    initializes constants. defconst emits directives to define a cell and initialize it to a constant value. v is the constant value, suffix identifies the type of the value, and size is the size of the value in bytes. The value of suffix indicates which field of v holds the value, as shown in the following table.

    suffix | v Field | size
    F v.d float, double, long double
    I v.i signed char, signed short, signed int, signed long
    U v.u unsigned char, unsigned short, unsigned int, unsigned long
    P v.p void *

    defconst must narrow v.x when size is less than sizeof v.x; e.g., to emit an unsigned char, defconst should emit (unsigned char)v.i.

    5.12 Upcalls

    lcc 4.x uses standard I/O and its I/O functions have been changed accordingly. lcc reads input from the standard input, emits code to the standard output, and writes diagnostics to the standard error output. It uses freopen to redirect these streams to explicit files, when necessary.

    bp, outflush, and outs have been eliminated.

    extern void fprint(FILE *f, const char *fmt, ...);
    extern void  print(const char *fmt, ...);

    print formatted data to file f (fprint) or the standard output (print). These functions are like standard C's printf and fprintf, but support only some of the standard conversion specifiers and do not support flags, precision, and field-width specifications. They support the following new conversion specifiers in addition to those described on page 99.

    Specifiers | Corresponding printf Specifiers
    %c %c
    %d %D %d %ld
    %u %U %u %lu
    %x %X %x %lx
    %f %e %g %e %f %g
    %p Converts the corresponding void * argument to unsigned long and prints it with the printf %#x specifier or just %x when the argument is null.
    %I Prints the number of spaces given by the corresponding argument.
    #define generic(op)  ((op)&0x3F0)
    #define specific(op) ((op)&0x3FF)

    generic(op) returns the generic variant of op; that is, without its type suffix and size indicator. specific(op) returns the type-specific variant of op; that is, without its size indicator.

    newconst has been replaced by

    extern Symbol intconst(int n);

    which installs the integer constant n in the symbol table, if necessary, and returns a pointer to the symbol-table entry.


    Chris Fraser / cwfraser@microsoft.com
    David Hanson / drh@microsoft.com
    $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $
    openarena_0.8.8.orig/code/tools/lcc/doc/lcc.10000644000175000017500000003430011656310262017427 0ustar smcvsmcv.\" $Id: lcc.1 145 2001-10-17 21:53:10Z timo $ .TH LCC 1 "local \- $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $" .SH NAME lcc \- ANSI C compiler .SH SYNOPSIS .B lcc [ .I option | .I file ]... .br .SH DESCRIPTION .PP .I lcc is an ANSI C compiler for a variety of platforms. .PP Arguments whose names end with `.c' (plus `.C' under Windows) are taken to be C source programs; they are preprocessed, compiled, and each object program is left on the file whose name is that of the source with `.o' (UNIX) or `.obj' (Windows) substituted for the extension. Arguments whose names end with `.i' are treated similarly, except they are not preprocessed. In the same way, arguments ending with `.s' (plus `.S', `.asm', and `.ASM', under Windows) are taken to be assembly source programs and are assembled, producing an object file. If there are no arguments, .I lcc summarizes its options on the standard error. .PP .I lcc deletes an object file if and only if exactly one source file is mentioned and no other file (source, object, library) or .B \-l option is mentioned. .PP If the environment variable .B LCCINPUTS is set, .I lcc assumes it gives a semicolon- or colon-separated list of directories in which to look for source and object files whose names do not begin with `/'. These directories are also added to the list of directories searched for libraries. If .B LCCINPUTS is defined, it must contain `.' in order for the current directory to be searched for input files. .PP .I lcc uses ANSI standard header files (see `FILES' below). Include files not found in the ANSI header files are taken from the normal default include areas, which usually includes .BR /usr/include . Under Windows, if the environment variable .B include is defined, it gives a semicolon-separated list of directories in which to search for header files. .PP .I lcc interprets the following options; unrecognized options are taken as loader options (see .IR ld (1)) unless .BR \-c , .BR \-S , or .B \-E precedes them. Except for .BR \-l , all options are processed before any of the files and apply to all of the files. Applicable options are passed to each compilation phase in the order given. .TP .B \-c Suppress the loading phase of the compilation, and force an object file to be produced even if only one program is compiled. .TP .B \-g Produce additional symbol table information for the local debuggers. .I lcc warns when .B \-g is unsupported. .TP .BI \-Wf\-g n , x Set the debugging level to .I n and emit source code as comments into the generated assembly code; .I x must be the assembly language comment character. If .I n is omitted, it defaults to 1, which is similar to .BR \-g . Omitting .BI , x just sets the debugging level to .IR n . .TP .B \-w Suppress warning diagnostics, such as those announcing unreferenced statics, locals, and parameters. The line .I #pragma ref id simulates a reference to the variable .IR id . .TP .BI \-d n Generate jump tables for switches whose density is at least .IR n , a floating point constant between zero and one. The default is 0.5. .TP .B \-A Warns about declarations and casts of function types without prototypes, assignments between pointers to ints and pointers to enums, and conversions from pointers to smaller integral types. A second .B \-A warns about unrecognized control lines, nonANSI language extensions and source characters in literals, unreferenced variables and static functions, declaring arrays of incomplete types, and exceeding .I some ANSI environmental limits, like more than 257 cases in switches. It also arranges for duplicate global definitions in separately compiled files to cause loader errors. .TP .B \-P Writes declarations for all defined globals on standard error. Function declarations include prototypes; editing this output can simplify conversion to ANSI C. This output may not correspond to the input when there are several typedefs for the same type. .TP .B \-n Arrange for the compiler to produce code that tests for dereferencing zero pointers. The code reports the offending file and line number and calls .IR abort (3). .TP .B \-O is ignored. .TP .B \-S Compile the named C programs, and leave the assembler-language output on corresponding files suffixed `.s' or `.asm'. .TP .B \-E Run only the preprocessor on the named C programs and unsuffixed file arguments, and send the result to the standard output. .TP .BI \-o " output" Name the output file .IR output . If .B \-c or .B \-S is specified and there is exactly one source file, this option names the object or assembly file, respectively. Otherwise, this option names the final executable file generated by the loader, and `a.out' (UNIX) or `a.exe' (Windows) is left undisturbed. .I lcc warns if .B \-o and .B \-c or .B \-S are given with more than one source file and ignores the .B \-o option. .TP .BI \-D name=def Define the .I name to the preprocessor, as if by `#define'. If .I =def is omitted, the name is defined as "1". .TP .BI \-U name Remove any initial definition of .IR name . .TP .BI \-I dir `#include' files whose names do not begin with `/' are always sought first in the directory of the .I file arguments, then in directories named in .B \-I options, then in directories on a standard list. .TP .B \-N Do not search .I any of the standard directories for `#include' files. Only those directories specified by subsequent explicit .B \-I options will be searched, in the order given. .TP .BI \-B str Use the compiler .BI "" str rcc instead of the default version. Note that .I str often requires a trailing slash. On Sparcs only, .B \-Bstatic and .BI \-Bdynamic are passed to the loader; see .IR ld (1). .TP .BI \-Wo\-lccdir= dir Find the preprocessor, compiler proper, and include directory in the directory .I dir/ or .I dir\\. If the environment variable .B LCCDIR is defined, it gives this directory. .I lcc warns when this option is unsupported. .TP .B \-Wf-unsigned_char=1 .br .ns .TP .B \-Wf-unsigned_char=0 makes plain .B char an unsigned (1) or signed (0) type; by default, .B char is signed. .TP .B \-Wf\-wchar_t=unsigned_char .br .ns .TP .B \-Wf\-wchar_t=unsigned_short .br .ns .TP .B \-Wf\-wchar_t=unsigned_int Makes wide characters the type indicated; by default, wide characters are unsigned short ints, and .B wchar_t is a typedef for unsigned short defined in stddef.h. The definition for .B wchar_t in stddef.h must correspond to the type specified. .TP .B \-v Print commands as they are executed; some of the executed programs are directed to print their version numbers. More than one occurrence of .B \-v causes the commands to be printed, but .I not executed. .TP .BR \-help " or " \-? Print a message on the standard error summarizing .IR lcc 's options and giving the values of the environment variables .B LCCINPUTS and .BR LCCDIR , if they are defined. Under Windows, the values of .B include and .B lib are also given, if they are defined. .TP .B \-b Produce code that counts the number of times each expression is executed. If loading takes place, arrange for a .B prof.out file to be written when the object program terminates. A listing annotated with execution counts can then be generated with .IR bprint (1). .I lcc warns when .B \-b is unsupported. .B \-Wf\-C is similar, but counts only the number of function calls. .TP .B \-p Produce code that counts the number of times each function is called. If loading takes place, replace the standard startup function by one that automatically calls .IR monitor (3) at the start and arranges to write a .B mon.out file when the object program terminates normally. An execution profile can then be generated with .IR prof (1). .I lcc warns when .B \-p is unsupported. .TP .B \-pg Causes the compiler to produce counting code like .BR \-p , but invokes a run-time recording mechanism that keeps more extensive statistics and produces a .B gmon.out file at normal termination. Also, a profiling library is searched, in lieu of the standard C library. An execution profile can then be generated with .IR gprof (1). .I lcc warns when .B \-pg is unsupported. .TP .BI \-t name .br .ns .TP .BI \-t Produce code to print the name of the function, an activation number, and the name and value of each argument at function entry. At function exit, produce code to print the name of the function, the activation number, and the return value. By default, .I printf does the printing; if .I name appears, it does. For null .I char* values, "(null)" is printed. .BI \-target .I name is accepted, but ignored. .TP .BI \-tempdir= dir Store temporary files in the directory .I dir/ or .I dir\\. The default is usually .BR /tmp . .TP .BI \-W xarg pass argument .I arg to the program indicated by .IR x ; .I x can be one of .BR p , .BR f , .BR a , or .BR l , which refer, respectively, to the preprocessor, the compiler proper, the assembler, and the loader. .I arg is passed as given; if a .B \- is expected, it must be given explicitly. .BI \-Wo arg specifies a system-specific option, .IR arg . .PP Other arguments are taken to be either loader option arguments, or C-compatible object programs, typically produced by an earlier .I lcc run, or perhaps libraries of C-compatible routines. Duplicate object files are ignored. These programs, together with the results of any compilations specified, are loaded (in the order given) to produce an executable program with name .BR a.out (UNIX) or .BR a.exe (Windows). .PP .I lcc assigns the most frequently referenced scalar parameters and locals to registers whenever possible. For each block, explicit register declarations are obeyed first; remaining registers are assigned to automatic locals if they are `referenced' at least 3 times. Each top-level occurrence of an identifier counts as 1 reference. Occurrences in a loop, either of the then/else arms of an if statement, or a case in a switch statement each count, respectively, as 10, 1/2, or 1/10 references. These values are adjusted accordingly for nested control structures. .B \-Wf\-a causes .I lcc to read a .B prof.out file from a previous execution and to use the data therein to compute reference counts (see .BR \-b ). .PP .I lcc is a cross compiler; .BI \-Wf\-target= target/os causes .I lcc to generate code for .I target running the operating system denoted by .IR os . The supported .I target/os combinations may include .PP .RS .ta \w'sparc/solarisxx'u .nf alpha/osf ALPHA, OSF 3.2 mips/irix big-endian MIPS, IRIX 5.2 mips/ultrix little-endian MIPS, ULTRIX 4.3 sparc/solaris SPARC, Solaris 2.3 x86/win32 x86, Windows NT 4.0/Windows 95/98 x86/linux x86, Linux symbolic text rendition of the generated code null no output .fi .RE .PP For .BR \-Wf\-target=symbolic , the option .B \-Wf-html causes the text rendition to be emitted as HTML. .B .SH LIMITATIONS .PP .I lcc accepts the C programming language as described in the ANSI standard. If .I lcc is used with the GNU C preprocessor, the .B \-Wp\-trigraphs option is required to enable trigraph sequences. .PP Plain int bit fields are signed. Bit fields are aligned like unsigned integers but are otherwise laid out as by most standard C compilers. Some compilers, such as the GNU C compiler, may choose other, incompatible layouts. .PP Likewise, calling conventions are intended to be compatible with the host C compiler, except possibly for passing and returning structures. Specifically, .I lcc passes and returns structures like host ANSI C compilers on most targets, but some older host C compilers use different conventions. Consequently, calls to/from such functions compiled with older C compilers may not work. Calling a function that returns a structure without declaring it as such violates the ANSI standard and may cause a fault. .SH FILES .PP The file names listed below are .IR typical , but vary among installations; installation-dependent variants can be displayed by running .I lcc with the .B \-v option. .PP .RS .ta \w'$LCCDIR/liblcc.{a,lib}XX'u .nf file.{c,C} input file file.{s,asm} assembly-language file file.{o,obj} object file a.{out,exe} loaded output /tmp/lcc* temporary files $LCCDIR/cpp preprocessor $LCCDIR/rcc compiler $LCCDIR/liblcc.{a,lib} \fIlcc\fP-specific library /lib/crt0.o runtime startup (UNIX) /lib/[gm]crt0.o startups for profiling (UNIX) /lib/libc.a standard library (UNIX) $LCCDIR/include ANSI standard headers /usr/local/include local headers /usr/include traditional headers prof.out file produced for \fIbprint\fR(1) mon.out file produced for \fIprof\fR(1) gmon.out file produced for \fIgprof\fR(1) .fi .RE .PP .I lcc predefines the macro .B __LCC__ on all systems. It may also predefine some installation-dependent symbols; option .B \-v exposes them. .SH "SEE ALSO" .PP C. W. Fraser and D. R. Hanson, .I A Retargetable C Compiler: Design and Implementation, Addison-Wesley, 1995. ISBN 0-8053-1670-1. .PP The World-Wide Web page at http://www.cs.princeton.edu/software/lcc/. .PP S. P. Harbison and G. L. Steele, Jr., .I C: A Reference Manual, 4th ed., Prentice-Hall, 1995. .PP B. W. Kernighan and D. M. Ritchie, .I The C Programming Language, 2nd ed., Prentice-Hall, 1988. .PP American National Standards Inst., .I American National Standard for Information Systems\(emProgramming .IR Language\(emC , ANSI X3.159-1989, New York, 1990. .br .SH BUGS Mail bug reports along with the shortest preprocessed program that exposes them and the details reported by .IR lcc 's .B \-v option to lcc-bugs@princeton.edu. The WWW page at URL http://www.cs.princeton.edu/software/lcc/ includes detailed instructions for reporting bugs. .PP The ANSI standard headers conform to the specifications in the Standard, which may be too restrictive for some applications, but necessary for portability. Functions given in the ANSI headers may be missing from some local C libraries (e.g., wide-character functions) or may not correspond exactly to the local versions; for example, the ANSI standard stdio.h specifies that .IR printf , .IR fprintf , and .I sprintf return the number of characters written to the file or array, but some existing libraries don't implement this convention. .PP On the MIPS and SPARC, old-style variadic functions must use varargs.h from MIPS or Sun. New-style is recommended. .PP With .BR \-b , files compiled .I without .B \-b may cause .I bprint to print erroneous call graphs. For example, if .B f calls .B g calls .B h and .B f and .B h are compiled with .BR \-b , but .B g is not, .B bprint will report that .B f called .BR h . The total number of calls is correct, however. openarena_0.8.8.orig/code/tools/lcc/doc/lcc.pdf0000644000175000017500000004004511656310262020043 0ustar smcvsmcv%PDF-1.2 %âãÏÓ 12 0 obj << /Linearized 1 /O 15 /H [ 736 168 ] /L 16421 /E 6171 /N 4 /T 16063 >> endobj xref 12 13 0000000016 00000 n 0000000607 00000 n 0000000682 00000 n 0000000904 00000 n 0000001058 00000 n 0000001183 00000 n 0000001291 00000 n 0000001397 00000 n 0000002767 00000 n 0000002874 00000 n 0000005942 00000 n 0000000736 00000 n 0000000884 00000 n trailer << /Size 25 /Info 11 0 R /Root 13 0 R /Prev 16053 /ID[] >> startxref 0 %%EOF 13 0 obj << /Type /Catalog /Pages 10 0 R /OpenAction 14 0 R >> endobj 14 0 obj << /S /GoTo /D [ 15 0 R /Fit ] >> endobj 23 0 obj << /S 50 /Filter /FlateDecode /Length 24 0 R >> stream H‰c```f``zÀÀÂÀÀmÏÀÀ 6PŽ#€¡ûÂËP @2PÌÀàÇÀÃr@ŸñLÀÙ]@5Ð) ¾ endstream endobj 24 0 obj 64 endobj 15 0 obj << /Type /Page /Parent 10 0 R /Resources 16 0 R /Contents 21 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 16 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 17 0 R /F4 18 0 R /F6 20 0 R >> /ExtGState << /GS1 22 0 R >> >> endobj 17 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /Encoding 19 0 R /BaseFont /Times-Italic >> endobj 18 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /Encoding 19 0 R /BaseFont /Times-Bold >> endobj 19 0 obj << /Type /Encoding /Differences [ 0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle 94 /circumflex 126 /tilde 128 /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /ff /fi /fl /ffi /ffl /dotlessi /dotlessj /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis ] >> endobj 20 0 obj << /Type /Font /Subtype /Type1 /Name /F6 /Encoding 19 0 R /BaseFont /Times-Roman >> endobj 21 0 obj << /Length 2993 /Filter /FlateDecode >> stream H‰œWÛnÛH}h`L ¬ï"‘'o&h0ã+Ù…=@ÚdKf†d l2Žûñ[Õ^;³ðƒh²/u9uêÔ?÷/_¼zì/_x.Á?øÙød†d_½|á’#¬ùeç‘£ÄÿöÙËÎooÞÜ:Þ튬ö_^¾')ìÜÿ<ú¦>½zšÃSuvªŽvuôs½ò]çê÷·«?ö¿ž·ÄƒµqSöpA™eäÖ"ru½Û’7$Õ©(yóý ãMJ}u„³ûÏõû»íN-ž]oì*¸ÂœÖ[DC/¶.Þ˜¾ùèÒ( ìGqj Q϶û4qûíÿ]l#ûíÖ÷Â’///ÌŠ?(¥O¸ÁiÚ‰ŸßîÞükûa¿}=»pæwØ-Oû}ã’°zwr aäëÊÖ¼}$â@N%káC%©NîZŸ?q¢Ns©*£7ÎU³òçØU¼n%y¸’“šU\^çä¡hïÉgš][çTvžß\®ÎáîO«ÐuŠ:xûƒH²†“–ý¹ò\‡×¤䎃±RtMÆÉ©džUò5iïùÊ‹œGm‹†ùÆ.Ï 5Òð¨SÃaSÆ¥äù¥õžÆYvOÄÝžµöd!*ù¡%¢Æ+ˆÎçÈ%\ÐÞ³c„ ´a*êûŸtT|ã·@¿?^oÿ ÎA¤áÅÝ|µt]vw²-Ú®å¹J ž¯¼üÖòZ*)Y­ýÈý±x:6Æ,L›2K¸á o‘EU”¬)Wqä\®Ö«oÌø©‚¬>àÆZ´“ÒÕ:Šg«£%1@˜:¶rñ¡lb1XYÔGk¨´ÀÛëk?ÓÝÅ%,b²ºÐYûL¯v¿Ãóß[«»òqŽ#u n0 ##Œ °8ï24ŠÇÀEã½w]g« €§¨‘‰³—–mõú.ÝD¡¥ƒçJVvUUùä¶€Èiv’ž²XPnѬ¢È¡óëÖ¾O½Í¨pÏ_X ¨x©ñ<ç%o¹\øMŠƒŠœ¨!¤ð¬!òþµ-Š~±$ °‡Û DfÜ:zÃ¥¹år 0k¨lÓ?Š»†5ªšfDÒ ð*Ž¢rᨛ&S¦Ÿ˜§™yÁÅSª3”rcr FÜ}-Qãæž=`ÈfÀZåyÔóÂQÝ^ø¸ßÍL‹©xvØ$9ÄaÊû>ú4Ï¢ßãÝU 4äX q_1GÊR"‡„P¡ê`_d¢õÉI?I~b⇲Èt&>82&Z7êR47Ýn¬‘ `V(½Rˆ?•D(ÐŒ‘4e¯\(‚¹Sx:âAŠ!^]`¡E©³¿ç°Ö\¶žÐ[h CÙ Jº”PŠyn€%˜4ã [+9k²{C¸Zðï =Hö<—þ†&~ò¹Œ“Q.sŽÞÖØw Ufdg ¶  IÏa~ã´ ð™®6®s¡i¿€ìÕúQ4¹éÜèXÖ5 ¢Ð:öhhoâ\QŸº>ì špŸeˆx30Dd£ÝIˆ¡=Ýs–÷µ-±¸9'Ÿßm{»»£JÍÔ·+Û̶uVv9‡>ázŽÝ…08 x„º¨î˜=°D¼™ÀrÚêúVphD¥ÎªA×°²rP­ª+[¸EÀaòÒ¸“+‘áôG¹CJ£¨Ïó«N6¯ÌÒYô"ˆØ¦/MÓ3?žë_[mAHc/š8–ZÇþ6ªÜ ˆ¯®Ð×Sæ33]šDÃÓt·®å¢ÃEq0îpKÝ¡×\Îv¯CŸ¦á(Üêˆ9{Ëûq4£›aÐ.!D€Ûf.À©Í€‰Ç¾]¹c‘‘ôòŠgª 3Qam€vz«õ'äÿL3u½'{<³Ïù êO4êx’Á^À#Á-Ø™ŒÒÅÕ~ÐÃ`ÄâéT*zU'ÚZõb˜:Ò3>ì6Zñ öÖèÔ¦ŒsÕP¢ç¦ôÉéžAÓ34¨y\Œ5‡hRç0ÐøLvÝ R!uE ¨±ôåfÖY¥¥8DN5õ™:ÔÍGËfpŠŒÕA-LpQ?.8M¨#GC™ÜèÒèÌÌßãSþºV³|Ðö¡D(Ð%èò±º%”7¦§¨Ø'Tl£-E¦ÇÝÊwîxäͲ“ú õ‚øi™õøÔÓRS£‚ðÌR‰´áÁ>wÍ£ŸŽ”FWËîd…EDÓQÿpN¢i,€Ÿã»zSÁ3wÕ³c\ù ÞéY¬ìÙïÛÌ…5r̤ÍV]츚AGGL–|ÒŠJÀRâ9-<Šy. ïm’…ÍSl’>ºˆghc­•ª™@9 €§‡VèZFyÍuëÇJ\üzæô¨*ÚŒóÂs¸„ËØ8­ä!Ô^Õ_P²úر#·æìúl]L}À-±³U´5‡£GÓ'CMº¾€´¦ç’Ä¢ö¬0Âñ¤¨Öv,å.¬LÑ`£Ÿâćfñ ¢Ý©TÒ|FAêçå Éÿ¿@çÒ8 ûk¾`È¥UÏ¢ Dëy´Á€}?Ò£Y¡µš$ó:|xŠÈÂxNÜ–KÐì¼`ÇZȶÈ@SÊ’ÆÐ;œµX]ƒ²Îp*œ‡Ùy¼^®XNjŠG9Wñõ’¥)˜irÈ‚f3š²¨ú'¢›p$Ô¶œ *C£#Í*Ø8ü@ŠÜîÆ)/:: ¯ƒ&„ý˜ôÖÛïœîÓpˆ¹xR„-Ypéoæ Êg˜éûßC€OÃ`£ž«ãçübè„|骓î=R5ùP´0½agÀ´å¼–EûˆÅÇZ€%“íìv?„átó#Tº(ˆ"%àáw$X«:¾(j5žâ@‡DÔ>pÛ˜¦A´”9ñF(´@Ó6%»W…4°$Zå\÷¹zõ^?Û}Z%¦q²;Ñ!Ye%ŽZR-H¢t9ØUoOÍÊ6€¸h…zu‰<[kÍó'ˆwC y$€=®ÀMªTF/yÝU¦‚ ”jPS ÒHeŸEÇë% ¤%(;|ƒ Aö”Ú^Œg9WÚžýOj:‚3óBê÷*ül=º ýí¥vˆädê‹Ú$5øS‹úêz·Ú‘2ö[‹Ð´q×ýÓ& Žcèy–¶ú¦æÂ²€'Å:N׳9^±TŸTØ 3ØeMÃUÎaÄÁXò–qƒ¦VüÙ¼cÓW.dœç£cK*¡IÚ´_Ö´;•Š ×é.¡Å0xпW =6BÍ…©SáØÑÞƒzö£ ¢•«@تǼ§©³míˆ1L—]B&”ßõÑÐFÞ© åXŠ;%WQ—×…. ¼‚#«·\ -«û°˜±Nr;Öò¦(rŸ~¨Y}j ¤îIµ¢Õ8NCÁm9dòW»nÂ0Ý‘ø f@à˜D!ª–~[Y€8¢Rš¢$ ü}ÏñCxTêÉÁ¹÷<+î»Êwu.7 ig™è@ë›wañå9wÂîg¶µˆí/Ê1þ<í‘ñçÜžÀ¶ŸÁ ‹ËsJ~×o¼Kl¡v׳߻ xB ­áÊ'0•)èŠÂA5üªx€ÁŸæÅЧŽ PÒ¦/! Ùÿú/5½·5ƒ ½ ‚úÿèpÁŠœ$b™ÜQZ=Ø\ +Gè} ÈF“`[›£œü£ç}êÚ±˜Hôú)øí…«|ùìê$IJýQÐÔ=®$â^–ÍCÔEªKz ‡šÕí]£^õVÛíT=C6ˆ ò´ã²T=NPMÞA–L™Õ*/Ò9\42™YfÖªÉtfb›FÚÈeÖ›ñèåQw¶ endstream endobj 22 0 obj << /Type /ExtGState /SA false /OP false /HT /Default >> endobj 1 0 obj << /Type /Page /Parent 10 0 R /Resources 2 0 R /Contents 3 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 2 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 17 0 R /F4 18 0 R /F6 20 0 R >> /ExtGState << /GS1 22 0 R >> >> endobj 3 0 obj << /Length 2785 /Filter /FlateDecode >> stream H‰œWÛŽÛÈ}7à v† F4擄0‚xìÚ‹õà Ö-ŠÒ´C‘ ›´vòõ9Õݼ¨[–'™²«ërêÔ©WwÏŸ½x“:s·}þ,ðú‡ÿ²ÐÉâØ¹Û?æ;;|ó·÷³ô×]ñü™ûóÍͽÜ/œÅÝ—çÏâÈKóNÞ½ž½“¯^¼‰µñeì{>¾Zâcõá}˜$ïôWƒ ôRÙÁïqáð]Ý´åÆ3 Ò·ËWOæÞÛ.—?¹7ÍþÀ«ÒéJ§fûrãÜ8‡¶Ùµl/®Voœªd‹Ðw¿.‚Ä-éÇY,ñK˜å~]•-}°¬X½ëÙ®tš¾;ôÓÔNÑ´m)M½áõιƒ¸*ÅâŸw§àn8‹î“+úí"L\úìyÛÆùì‰+§iñ?û+O½ö_/…Fú®ßúV2ðC["æ¢WÁísɹèë3>ª°Ö.‚ÜÝõû²îtêD‰ûFÄ.rÓWÓ5òFÑá k7:O)q£¿ ÐzI6@OY1’z~4sà b÷-•^èâ©  ó+œN¾c? ã1Ý)Y/(ÝÞí¸[³€·Jà 䅎«0Ì^„ãÅ­‘„ÃÛsÈGZF¯>Q‰CYp åcªFméà…ø¬è€Œ¦FMš¾-Jk|Ç5|—Aæeyv’Ë¡§šCÇ"BP9])‹Žp¬;æq´Híwœ@¥Z¬z\¤‰ë-–iî»ïȳ#únÇ2MvjV)§îþ¤jšhGT4ÒjÑw :€uWÖeË:„¿V-P5lƒ>Ž}÷z±ŒV‰KiùÌHÅc\5í( +Ñmð™÷@ÈÜ;9:gòç­4óòl¬òw]sg·h.J¼(‹MÈ»û:ºhZLÑCSG&w&žҙ_/(Nš1*Q¿¶È:i–=òޤÉb1aoŒ’LN?%ã$[½,®h°êi•a¾¥9±kÙ^•Xñ™šyÎpOÿ„‹Ã‹gZÅ©ûý(MFî/MK" ­ÜÔp (zdª.È«B´ò¥óao&„fu4ÒCÁzQŠAÁ©lÀçu©Ü&æ^/BßíAØçÄRO _cBaåÞœe1oŒ¬ÙZõLIÊê`¯y“TilUéçÑ<6CÞ£ì¤ß¡ê$2˜ƒ­P°]I«ÏÉNQ¶-h\µü?ÐzÆ F²q;;«‚Õ4#¯I‚Aˆ¨SüzŸ4†—$'É5š«žÖ1 A=¼—ÃP ¼Uº:‰*5¸Î xa&?ƒ¸žOøÛ·¿~¸{oB>„—Äf6-8gU–‘xšO$ª¢ØåÀ×V=Î:Énh‹¨*Ý5-=)zPoC!Õæ:Ë’NJ°‚ Ÿ­lí—Т…år”^Œy&°+¾¶w†ñô0ã),V‰ætuƒLÚΘˆÖ#~kµžüÀìÓƒ9 ú¿ 6›¾ ÆßHz!Bì1Œd"I¨qô‚S²âaØlAÓBÉ)‹‡-ëvK;ÖÊ¥¥A"x®iŠòú úi¢Zµ-«Ñz4À˜‘êÄK’pÏ¡]`IDEq¿×ô¹-Ò,š FI¬JMoÇ–w´½iˆF_e4î`Zïy¡N$œ@•þxÄ5ÄjðÍ|³´ï©‚Ò|ä݃‘'JžÎuÁäõ5ù´+ë²%›1ÙÌ’æ#ÌÖ’Ÿ-ZL‚‰øæ«Öhj!½¨¤Çצ’¶uhž°¬û8Mg’ð¢ÆF‹­²³2ïÆs<ê_>‚ïyņE0¨׈ꦮ hoûºPÅ`U%ìîʤÔ6šËœE' #ÍþïæáBú#é.M3Zæþ‡6 t»Øké›Óñ…‡¶ëÓe¬$*¤‡¬ïš=ë8Ýþ¨rbÂk@:Ê»}©Ø˜›@ä…ñ„Áë' Aú\†ÑnüèšèY ÄeN>ÐfPH+¨¸sƒ•‚¡¥¥…¾…>Ð\#C+C+ccM]CSc # #H¸†ðrU9 endstream endobj 4 0 obj << /Type /Page /Parent 10 0 R /Resources 5 0 R /Contents 6 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 5 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 17 0 R /F4 18 0 R /F6 20 0 R >> /ExtGState << /GS1 22 0 R >> >> endobj 6 0 obj << /Length 3456 /Filter /FlateDecode >> stream H‰ÄWÛŽÛÈ}7àhìK¨…Äáýc¼³qâÀ‰Yx+@(ª¥¡—·t“™™¿Ï©n²I‘ÚYç)0¬ÅîêêªS§NýxÿúÕÍ»ˆ¹ìþôú•ë0ú‡?±Çâ `÷ÕëW;cÍ_î\v–ôtŸ¿~e}¸½Ý[î~Ã6÷__¿ |;JRì¼ÿiöN½Úù‘àÝKÔkÇv|Ý?ba&DVŸ¹d]ÃEÑq–©=7ï‚Á¡ØŽSo´[5µÝôݰdôÙ·wXñÅÚ{nPröøÀkÖ=pÖ¾ò¼c­hÎ"«XÇEUÔY‡#ëFTYY>o¢Ð²7»Ð÷¬·Ǫ7;/ñ,¾qCë‰>xÞwES“‰ÝæŸ÷Ã\׎Ÿí\ÛÓ7Ò·ÎÎ3utÍœyÍŽ;²Ç¢{÷Æ`{vFÆõVl‚ÐjNúYNp¿d Š«½4d§îhÇ*ó| ×Üé˜Çë ðµTAãÚ²>æ…a»2æÇæ¬B²¾–}Û6w´¹Ûy±EÅ)JfÏ«ŽÔUÁübÝf½$d ‰ySµEÉÁi8ö9ýÖ×]QŸñåÈYYüF×âËëxH‘¾|×›‚³ÝìüÀµϱúŽõ&p¬ÿÐSl g8–ÔË2ýGôõ®+*ÎÏq$§*ž?d#\<ÏöƒZl7J‡KÖ…(²ŽiÛ¼•¬jg¯e±ñB¸@H¤¶ÙáSvYWÈ®È%Ëêã<¬Øž3–‡u¾^Añ$…b¸¤+ÄÔ Š@gbGØ0¿‘7\èm)›-ËÈ#eˆ‚Q‘‰g´Hž‰ü·-~ç=kN*ŸP}ÌđݎˇºŒû¡.ýع^—Sáéëßojò› 1µ}ß@àü{uˆ,þ?ëpY5 …o/ÄЗeØ-œwìÔ7 ^g_Ñ LÒ/Œ\-å(@ñÉÔ,JUÕpQw*ítÎS_ç”QÀ§f¾jàãCaÕ}uà‚ ’ªÔ-B¾1BzqÙ“IPgRÇâYþÀ2±qëÜWÎîãù Ïbj ±‚_Gm!¶¦5 ‡E·Ò7]n†PÛ Ç²¡%xeÇ3W¼ëE=]Ø-x¡cýø¬×ùIÁ«/»í­1Ñ £ÔàXy{ZdѳCÏæ±XX-E=¿aÅiÄö½+Йì‘91k[€Üqà¥Á²•~±ŠŽÑÁ”DZÞQ´‰…¾fÝ—åòZÚdjª+§™ø~]Å¡ñå‹e(·ì»½²»ß|GL¥nK¥´(L×Á9ñLo IŽuæÝÒ'ßö£àåÀ̧fyÎÛŽøÑ´ 3høŠ;/µÃ$XU5¯Úc!~X$(°# ï~ÀˆãÎkû®£&DÆEÝša%±6A ªTÉ‚;MãÙ97+ÒŠRƒ¬F¬øÓIüÙîýÞ^Ì5û¿X÷äÊ„tJ[/{%êÑJ@–Ư›®ZªƒÞuúº9.ÂüyåyL x Àçµ’KCÿ’‘‚ïm&å#­áín÷€È7=\¤ËVKߢ>¹ê€‡UÚЙb‚§El¨T»7«®F/ìŒ=S÷$‹Ñˆ›Z±óe”C;Lâ—¹Á¡¹Ôv±/}SaKsÐ×ýoÚ™­Ï4}ÌC•ÑÈuØlðZr‚P—+K^<ÆÀíP3 ¬Ç‡mHð“¡xeË–ç£vôQQz!µü±gÌ”¡c¨I$Ô«H®ê¯ €:”i%Ã+#¬± åË·€$¯åÅïc*›ìˆ!5É%éy3%ñTã©gßÒiGœ9ô0 9¯•d¼øyvñšºÑZövè^ȧUVRÇŸ¯îç¼"_tŸª—Žsý­e¨ï2´-‹¼èÆr Šœ{Ñ'>obêbËvl;Þ·vb Fø(ˆ‘Ið3ù,ÁÓ;ócΚV+ËJ%Ï:í gùqpɇ;ðz<ï5H‡;R×GàA\p1ºG¦gššT"É µP#gpòrÛ–5‚Ýî•Ð?@k_‘’‘ð {nÁb†áËʈ´è$1iIÈ ŒËf“ÚqdÈášXCϤ#žrEòaFÓ3J@ß]x,0^µR.àbë§ž@Ú¯2vRDh(q°¯ï†P§(k4tÿAÞ¡áINâ7±f!i <(²4Ù êP¢*ß0©Ïz.w%-1Šð”/•˜#Û[C“Ç(ËÅr ZTß~£%¯–ÀÙ¨‹Çù,£ˆŒ H¹§DÐrFÇùÝÙ•£WbäËÞúåïÅÉF?LULÊ6³ùÓZoùÉTk{ë31cCWz”4Ñ-ªjÚötÞÿ¯v+ÔÔ£!T 4$P«çªŸœÿw¤—ÏšöyM(•tleˆï¸j˜)ü,)¼Bõ\HõަE~ÁI$Š“Èj‡€`™‰æhÊÍyÁDÞ| :àÈß¶lÎmgCkåptÀA¦9¨×Ï IÈî vTYQcNXøMëu@°·Êú®©`+¿˜ˆ‚‘U†ëj|0ŒIÁ"3ÿš‚÷'šÜJž!¸>ëŠJ A`ý™nÔ5í®¼ V©Í4yÞ eBW +ŽÈ‹®¾Ø+“H°îÏã»ÉQ‹6Úì£1¢”q†œ5ív$ºaòÃÿú†—·•O;1Ùˆö±d,ϰDY‘¨8o(´ ñúeLù¹eZ2\׿\[꯮³eî§Žto\g‚¡Š[jŠ™Ë!oǯh‡Ôžó´€äÁ'ØÑáçË™V·iM݉¦Ä5DŸcZ¥.Ë5¶/ž7éÏ'ú\j1 *šTd/¹\P9‡û"•Q:SÅ‚g¸ÉÂßNÂI´ª-A§"˜Š=ÒG+õfÍ8ÕÝTHô–JDÑôrÁ‡ªãÕ þ¸†¥²nP/;χÈqgªI`â…ê¢Dà}ǧ¬HÝ[’óÅ @?™Ç÷° Œë™Èù-ƒCÝÈ}Ѥ• ÕNÓ,ËrJ2"óÍ¿³œ)Tþ;ÒŽ…¦öÃ’C;2ºQ¾Æèn¹ÎQâÄ„'y7á 7g¯ Vž7GNð_˜KìÀõ¯û·!U¾ðÏÃÌ8Z‡¾P”©únK§àÉ(á ]£BË>PrÝ\éb;JùF®õ¯;…’JßQê‚ɾmajz>ÆAú¿?M͇¢úF•=ƒæò²?jÀîÜb=ž¡Ì›iý¬l2Ø?±·>ýõíV·¶wï(9ñ-ÃZ {«¢•7…(ž¨!ºp÷Pœw¼>EÊÌßߺ۲÷?¿ÿ•…£b±a+4”Þìúaj•EוüÊþ_>lRϺ'+íÏ­H´ôüF6hš¨‡Í.Æž»O´úíÏ·[v7¼ð.w=%ÑÍcQc:ÂÉAü_¾Ë®7Q Ã÷›ì˜‹^Ф" ˆCïºÝ/ën¢ûPl%À¦Û¿ï™ÁmÒÄŠ:çÌù|Þ˜{x…­60L‡ÓÙ4îÈɶkƒ—EuúËÜ©Kz?½ö½ÜÖ Š^ ¸×éYFL€¨YfVœmŒ\u†;)V®O«NR²ªÖ1Qv¨N"4µKþ„¤¼ÝѾãû°ÙŒ´•Ÿp[ÜùîÕbï0,ÏÔ’ÞÊC Ý‚t½±¨œÏÅ徚ºRz ý*\oéÁ£I`@£ŒJÔm×–ýÜ<-GR’)`)›£.éàåâi±¹Mïá–Ööâ×j=ìÙ0P? éO°"\þoÒE±â¡•X÷ûc§½´ _Ò ’YõrÊ^é0ƒÚ]Slq£VëáK•gMNûtñ<ôdÌfŽß³S´OÇÜiœ«?ìÑLÈÈŸñ¨¯l±‚á, ¦ÅFo¸ÔC;ÇÊ /rzT%Ô¸êñ0œ›‰ŸÆÉEÝxFÈÂA‚û¢Ñt»¯”ö±ç°VÿÎBFŠŸŠ~Ý9Yÿ-AÐfǶ…VŒ{™kÓüL1qïËðÓLj¼–Å«^;/Ü;U†ºq¢Frâñ-1í©ÓOŒñVI`9g5=ni§ŒF«–Е¢±YFyØ=¯H2ˆ½u]îÝÃ;¬kÖºì?<§(öh;ìuݧzaiàûØ"lÅeöM\ƒËˆÎ«Kš¸æh[JªcP*™0rÖ) U•ë\nõ5´eWºÿè';ÔçÔ:̈2EËעɭV<’6‚C„‡Í¼¬x Ï“O¼5@_©º€üa× .âûªÇ®dwßö̶=£½Bƒ{êÆª¹{Ù¶ðøœ'êò¼tЪXÔ`4˜Q¶þZ*“ZæPLƒˆª–gyñLiSÀÛ]Ë—Õ»ø_uYçÔ^(P(ºzJx~¡TÛoª2ŸOÕNçߨ7ÃF»Ö÷ŠjÔÅÞ(4uózv@×̆¢K½ØSQ1YwŽpæb¬= ¹ÏgØ…È\)Éþ3Iìæ+Vì= ÓTL1Åäá}ßG»¡5aGø¶ùüéŸ`Q~ endstream endobj 7 0 obj << /Type /Page /Parent 10 0 R /Resources 8 0 R /Contents 9 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 8 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 17 0 R /F4 18 0 R /F6 20 0 R >> /ExtGState << /GS1 22 0 R >> >> endobj 9 0 obj << /Length 2530 /Filter /FlateDecode >> stream H‰ŒWkoÛ8ý^ ÿ Œ4°i½™/›¦¯Ì¦"NÑY$ƒB–i[³zA¤š5ýï{/)J²”¤EQÇ–ÈËû8÷ÜÃW·g/Vob“ÛÝÙ Û"øþ„ =Üg/,²‡5ïÖ6Ùsüu›ž½0®//ï ûÞ$æíßg/<—Q ;o_ÞÉWKÏ¢¼[Âù=œ½¸32q¨ZA¶,Í“&+÷$$á„·é|˪<Œq`äâãúŠp‘”Û¤ÙøCŠäHÒ¤åŒ$dgÚ–‘´¹ æ_·¿c4^M,ƒ‰1– ¶©#£1Þ^]¿YK׋۶"„q·\w:woÁ‘{ÇörFʤÏòŒ ¶%–W¦ãæÒ Á‘†i7mÜ¡‘íêäˆc¥I>qÀ¡a¨—Ü ekc:–ú†ö“æH’¢Â4•‹Ò“U%ÿíäçrËjVnYÙoÊ’RpÈU Ž*Ç–¶Oý( K™‘¾ Æ6ãuž1¤#iÚ²„’t^êH"Ä} yšN³H=;Я±¼X½n®‰K}½âÞñýo3®£ßW5†DŠl|£ÓA$_4umT]è?éâò;ä£n$зºç*lk8@º“­|‘ðâ;à³b“—yRîÛdÏæ†nE ®åO,T‹jó÷w,}ä|ܦ¡ŸÀêV,˜iûÆÿðƒ™–ñÝ\¾ky•l¡°ãè÷†Ôò´Ó+QÔ+Èÿ¯D°¢®ÀÆø@>ìòhìª,/¡-__ݬҺ&uÃê¦JçU#ó‹k}jûþéÚ&MIZu–³a]ÿ6Ï6àý'YÀ·ï¬@Á¬ð9¬ØÆJÀ%¯Yša)4×FF¥jïPÇ Æõ‹-O§V®ÒFX´BЊ¬èJMш¶&f‹{ãóÇ«?’fµîöÅ_séÅA¿+c»ªœU2½ÈTssPÛ›„ÿ)MzÚRvº°ˆÂêGœr©çjPë,geš·[Å‚}hŠ M–€iøØJ†Ú—–7«¼Æé É_ÊÒl+´F·êM¢I¶öã“[C8úTÈÕŽ"¹w wÀÃm›´ÍVbNgLÒ8êùaSÃT3¢ô¼A†)ãXº"!õûri>ç ,ü¡/‘éùFµÓËŽóˆ?vLÀÿÈ¡ûxdý”Gû§\r¨ý˜Kƒ¡¥ãÒP9בéãdîêÃ` Ú à&Û2ô»ì†s‘¤M5!y˜n?¾~ ý:1ïQ7ŒûXª’À#ü#µàÔ\úvl\ 9瓜Wdt,áUÁžšzüX,õ˜ó( e:å6U3S–‰×>µ,û'‡Ó¡È»®¸ÊCñœúðP(õ±~ó†\\¯ÿxV€x–Õ­Gt°¹¤ä‹;Euo›„³FʡהÜPò>)yU.f°qaÜE½ßj÷ ‚3ÝÐØ£|‚6—ä²#ûsòšñl_JóWE³Ò+³½˜áÍ¥N<Ôòb JŠòÅŒÀ0Ïe¢ŽfàÝÎ¥ ã3 g¥±ãاäjýê#±–‘å»K;-X%seàªÔ˜< jò-åYF¶í± ©ar“Dƒõùjõðð€çÓ”Sä”” h@¶mW¼Ú‰©†uE>»3Ö”|2mÛ–‡}Ÿ4 M&å%×”¬c9[ßÓ‡æÉœÃ¬Ò¹¹<'ä†í˜L=Oȇ¤l“|–ÔXîŒH)¶¥ ò©*d)[¾ì/ˆÌXÇÌÈð$G¯Fxïÿ‚5e¶?$¥†Í@N&ÒCÆs=ð5n–D”©„ä‰(¾¯LÏ5ª}cÚ®‘NÈëN?̓Vˆp˜üi_\z<Ê(ÒQ¯Gþ(J‹Úv¤¶_¬ÉPè~LÔœ‚â¨9Éɰg¢P<õ ÷ô~,Ìo7ˆ¤¸*á³kÈZ“óI±1‚ij®AÔ«\ |„Çõ¨¿3TŽÔlB4s9Í—s$:Jê~•÷¢?]m1Ì›(^L_Eàõ»Ø ÿ•@±ž#('Ö°A_>¿[Ï wBQN4¢¨P ÓI–umÙИU—$Ç[‹¾~€§Œ‹‘…©ßöMRÀ hÛ)·J´âÞ-U–óÎ4ÛΤ½±9N«<ºQ<*Cãp„qÀx½ý…O†ƒ³÷µOˆxšZ©qCDEÀƒe—þ¯.¢DrÚ—/=y}¾¹þ)Óð»SV½3NøtzŽw©„Ìã$mÚTÞ& \%›X{JGyîÏòü H,ö´‡pwÍ‚‘ËÒ÷_]^álù\7ê‚<²ô Çþ4gšm)2ö7uW‚éc‘4BғλT I]çYwࢠI’!ðP‹Km'›,Ï„T(@¬ÐxÛ–]Zöãã-£Ä lø*t tä]EÆ9fr×TÅèîÉ‹Ç>JILªî”çÞ`t\øcm™’&ILûvéÞ$à7žRVòÜ@¢ê Tö¬Î:íʺò¾á¸û·_\í ¦¢Û#AĬ²\l³Š†RÊMÄtz8ˆ”¾ñ¤’ßÍ¥¼­›Å˜’³Eý¾“ŒÝûƒ¾w'F±nÎ\¡NØ{Ÿ0&AÃDÛ(d–m±´W;‚5@ìèns¸bÌ»M–‰“‡&‚•ºJècÅ’¦I:1$IZãQÂ@å>ã²ù,l«òÓŽ ¡¶dZŽéLöÊUÞÁñD»vþ¡"ûpõi-yuý EÂÅÍå‚T ¥¸8‚§ßïp:\úÒt¤h·[Þ¿‡”€_@‰pe¢\·%Õ賘ñ9Y7,­Š¥ûö1‘§d8Lt¸^ oæ¬î ÷ z¡´¯ªfNŠÝ`ê½}œ[pW›Œ¼aø¼ žÕƒÛ4M oà %þÑu7t` ¼Á"š¦*YÕB xo‚ÉYäµÉ{¡’DF{ÒÐÙnšL¸¨Z®×wÚä| ÔG߆xÖt..=yÁèžZfj"ôƒçL8p?éÏ8ÌvGvŸGlîÓ½6 ŸvÝÌ>»ó¹3qœ(øà í!#åÌÄbÕüyT:”€Jà¨S Ýbv«GÀ³¹íT 0L„Å,Ç¡þiq T) ØýÔZ@Ûý©¼c½Ù4õ@Üž÷tòý>¤;5‰©0D%`Š)BÖìSß{„„‘®hDÒ#ŒÆT,È¡’tÄN&¹º¶)sp3q`d-cyÝñjrb1ÉË׉`稤£•­8ö¹í».y‰bäÿ3¶0Ò0äÂËÙ€x1 endstream endobj 10 0 obj << /Type /Pages /Kids [ 15 0 R 1 0 R 4 0 R 7 0 R ] /Count 4 >> endobj 11 0 obj << /CreationDate (D:19980824141523) /Producer (Acrobat Distiller 3.0 for Windows) /ModDate (D:19980824141613) >> endobj xref 0 12 0000000000 65535 f 0000006020 00000 n 0000006171 00000 n 0000006295 00000 n 0000009154 00000 n 0000009305 00000 n 0000009429 00000 n 0000012959 00000 n 0000013110 00000 n 0000013234 00000 n 0000015838 00000 n 0000015922 00000 n trailer << /Size 12 /ID[] >> startxref 173 %%EOF openarena_0.8.8.orig/code/tools/lcc/cpp/0000755000175000017500000000000011720470210016611 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/cpp/eval.c0000644000175000017500000002324711656310262017724 0ustar smcvsmcv#include #include #include "cpp.h" #define NSTAK 32 #define SGN 0 #define UNS 1 #define UND 2 #define UNSMARK 0x1000 struct value { long val; int type; }; /* conversion types */ #define RELAT 1 #define ARITH 2 #define LOGIC 3 #define SPCL 4 #define SHIFT 5 #define UNARY 6 /* operator priority, arity, and conversion type, indexed by tokentype */ struct pri { char pri; char arity; char ctype; } priority[] = { { 0, 0, 0 }, /* END */ { 0, 0, 0 }, /* UNCLASS */ { 0, 0, 0 }, /* NAME */ { 0, 0, 0 }, /* NUMBER */ { 0, 0, 0 }, /* STRING */ { 0, 0, 0 }, /* CCON */ { 0, 0, 0 }, /* NL */ { 0, 0, 0 }, /* WS */ { 0, 0, 0 }, /* DSHARP */ { 11, 2, RELAT }, /* EQ */ { 11, 2, RELAT }, /* NEQ */ { 12, 2, RELAT }, /* LEQ */ { 12, 2, RELAT }, /* GEQ */ { 13, 2, SHIFT }, /* LSH */ { 13, 2, SHIFT }, /* RSH */ { 7, 2, LOGIC }, /* LAND */ { 6, 2, LOGIC }, /* LOR */ { 0, 0, 0 }, /* PPLUS */ { 0, 0, 0 }, /* MMINUS */ { 0, 0, 0 }, /* ARROW */ { 0, 0, 0 }, /* SBRA */ { 0, 0, 0 }, /* SKET */ { 3, 0, 0 }, /* LP */ { 3, 0, 0 }, /* RP */ { 0, 0, 0 }, /* DOT */ { 10, 2, ARITH }, /* AND */ { 15, 2, ARITH }, /* STAR */ { 14, 2, ARITH }, /* PLUS */ { 14, 2, ARITH }, /* MINUS */ { 16, 1, UNARY }, /* TILDE */ { 16, 1, UNARY }, /* NOT */ { 15, 2, ARITH }, /* SLASH */ { 15, 2, ARITH }, /* PCT */ { 12, 2, RELAT }, /* LT */ { 12, 2, RELAT }, /* GT */ { 9, 2, ARITH }, /* CIRC */ { 8, 2, ARITH }, /* OR */ { 5, 2, SPCL }, /* QUEST */ { 5, 2, SPCL }, /* COLON */ { 0, 0, 0 }, /* ASGN */ { 4, 2, 0 }, /* COMMA */ { 0, 0, 0 }, /* SHARP */ { 0, 0, 0 }, /* SEMIC */ { 0, 0, 0 }, /* CBRA */ { 0, 0, 0 }, /* CKET */ { 0, 0, 0 }, /* ASPLUS */ { 0, 0, 0 }, /* ASMINUS */ { 0, 0, 0 }, /* ASSTAR */ { 0, 0, 0 }, /* ASSLASH */ { 0, 0, 0 }, /* ASPCT */ { 0, 0, 0 }, /* ASCIRC */ { 0, 0, 0 }, /* ASLSH */ { 0, 0, 0 }, /* ASRSH */ { 0, 0, 0 }, /* ASOR */ { 0, 0, 0 }, /* ASAND */ { 0, 0, 0 }, /* ELLIPS */ { 0, 0, 0 }, /* DSHARP1 */ { 0, 0, 0 }, /* NAME1 */ { 16, 1, UNARY }, /* DEFINED */ { 16, 0, UNARY }, /* UMINUS */ }; int evalop(struct pri); struct value tokval(Token *); struct value vals[NSTAK], *vp; enum toktype ops[NSTAK], *op; /* * Evaluate an #if #elif #ifdef #ifndef line. trp->tp points to the keyword. */ long eval(Tokenrow *trp, int kw) { Token *tp; Nlist *np; int ntok, rand; trp->tp++; if (kw==KIFDEF || kw==KIFNDEF) { if (trp->lp - trp->bp != 4 || trp->tp->type!=NAME) { error(ERROR, "Syntax error in #ifdef/#ifndef"); return 0; } np = lookup(trp->tp, 0); return (kw==KIFDEF) == (np && np->flag&(ISDEFINED|ISMAC)); } ntok = trp->tp - trp->bp; kwdefined->val = KDEFINED; /* activate special meaning of defined */ expandrow(trp, ""); kwdefined->val = NAME; vp = vals; op = ops; *op++ = END; for (rand=0, tp = trp->bp+ntok; tp < trp->lp; tp++) { switch(tp->type) { case WS: case NL: continue; /* nilary */ case NAME: case NAME1: case NUMBER: case CCON: case STRING: if (rand) goto syntax; *vp++ = tokval(tp); rand = 1; continue; /* unary */ case DEFINED: case TILDE: case NOT: if (rand) goto syntax; *op++ = tp->type; continue; /* unary-binary */ case PLUS: case MINUS: case STAR: case AND: if (rand==0) { if (tp->type==MINUS) *op++ = UMINUS; if (tp->type==STAR || tp->type==AND) { error(ERROR, "Illegal operator * or & in #if/#elsif"); return 0; } continue; } /* flow through */ /* plain binary */ case EQ: case NEQ: case LEQ: case GEQ: case LSH: case RSH: case LAND: case LOR: case SLASH: case PCT: case LT: case GT: case CIRC: case OR: case QUEST: case COLON: case COMMA: if (rand==0) goto syntax; if (evalop(priority[tp->type])!=0) return 0; *op++ = tp->type; rand = 0; continue; case LP: if (rand) goto syntax; *op++ = LP; continue; case RP: if (!rand) goto syntax; if (evalop(priority[RP])!=0) return 0; if (op<=ops || op[-1]!=LP) { goto syntax; } op--; continue; default: error(ERROR,"Bad operator (%t) in #if/#elsif", tp); return 0; } } if (rand==0) goto syntax; if (evalop(priority[END])!=0) return 0; if (op!=&ops[1] || vp!=&vals[1]) { error(ERROR, "Botch in #if/#elsif"); return 0; } if (vals[0].type==UND) error(ERROR, "Undefined expression value"); return vals[0].val; syntax: error(ERROR, "Syntax error in #if/#elsif"); return 0; } int evalop(struct pri pri) { struct value v1, v2; long rv1, rv2; int rtype, oper; /* prevent compiler whining. */ v1.val = v2.val = 0; v1.type = v2.type = 0; rv2=0; rtype=0; while (pri.pri < priority[op[-1]].pri) { oper = *--op; if (priority[oper].arity==2) { v2 = *--vp; rv2 = v2.val; } v1 = *--vp; rv1 = v1.val; /*lint -e574 -e644 */ switch (priority[oper].ctype) { case 0: default: error(WARNING, "Syntax error in #if/#endif"); return 1; case ARITH: case RELAT: if (v1.type==UNS || v2.type==UNS) rtype = UNS; else rtype = SGN; if (v1.type==UND || v2.type==UND) rtype = UND; if (priority[oper].ctype==RELAT && rtype==UNS) { oper |= UNSMARK; rtype = SGN; } break; case SHIFT: if (v1.type==UND || v2.type==UND) rtype = UND; else rtype = v1.type; if (rtype==UNS) oper |= UNSMARK; break; case UNARY: rtype = v1.type; break; case LOGIC: case SPCL: break; } switch (oper) { case EQ: case EQ|UNSMARK: rv1 = rv1==rv2; break; case NEQ: case NEQ|UNSMARK: rv1 = rv1!=rv2; break; case LEQ: rv1 = rv1<=rv2; break; case GEQ: rv1 = rv1>=rv2; break; case LT: rv1 = rv1rv2; break; case LEQ|UNSMARK: rv1 = (unsigned long)rv1<=rv2; break; case GEQ|UNSMARK: rv1 = (unsigned long)rv1>=rv2; break; case LT|UNSMARK: rv1 = (unsigned long)rv1rv2; break; case LSH: rv1 <<= rv2; break; case LSH|UNSMARK: rv1 = (unsigned long)rv1<>= rv2; break; case RSH|UNSMARK: rv1 = (unsigned long)rv1>>rv2; break; case LAND: rtype = UND; if (v1.type==UND) break; if (rv1!=0) { if (v2.type==UND) break; rv1 = rv2!=0; } else rv1 = 0; rtype = SGN; break; case LOR: rtype = UND; if (v1.type==UND) break; if (rv1==0) { if (v2.type==UND) break; rv1 = rv2!=0; } else rv1 = 1; rtype = SGN; break; case AND: rv1 &= rv2; break; case STAR: rv1 *= rv2; break; case PLUS: rv1 += rv2; break; case MINUS: rv1 -= rv2; break; case UMINUS: if (v1.type==UND) rtype = UND; rv1 = -rv1; break; case OR: rv1 |= rv2; break; case CIRC: rv1 ^= rv2; break; case TILDE: rv1 = ~rv1; break; case NOT: rv1 = !rv1; if (rtype!=UND) rtype = SGN; break; case SLASH: if (rv2==0) { rtype = UND; break; } if (rtype==UNS) rv1 /= (unsigned long)rv2; else rv1 /= rv2; break; case PCT: if (rv2==0) { rtype = UND; break; } if (rtype==UNS) rv1 %= (unsigned long)rv2; else rv1 %= rv2; break; case COLON: if (op[-1] != QUEST) error(ERROR, "Bad ?: in #if/endif"); else { op--; if ((--vp)->val==0) v1 = v2; rtype = v1.type; rv1 = v1.val; } break; case DEFINED: break; default: error(ERROR, "Eval botch (unknown operator)"); return 1; } /*lint +e574 +e644 */ v1.val = rv1; v1.type = rtype; *vp++ = v1; } return 0; } struct value tokval(Token *tp) { struct value v; Nlist *np; int i, base, c; unsigned long n; uchar *p; v.type = SGN; v.val = 0; switch (tp->type) { case NAME: v.val = 0; break; case NAME1: if ((np = lookup(tp, 0)) != NULL && np->flag&(ISDEFINED|ISMAC)) v.val = 1; break; case NUMBER: n = 0; base = 10; p = tp->t; c = p[tp->len]; p[tp->len] = '\0'; if (*p=='0') { base = 8; if (p[1]=='x' || p[1]=='X') { base = 16; p++; } p++; } for (;; p++) { if ((i = digit(*p)) < 0) break; if (i>=base) error(WARNING, "Bad digit in number %t", tp); n *= base; n += i; } if (n>=0x80000000 && base!=10) v.type = UNS; for (; *p; p++) { if (*p=='u' || *p=='U') v.type = UNS; else if (*p=='l' || *p=='L') ; else { error(ERROR, "Bad number %t in #if/#elsif", tp); break; } } v.val = n; tp->t[tp->len] = c; break; case CCON: n = 0; p = tp->t; if (*p=='L') { p += 1; error(WARNING, "Wide char constant value undefined"); } p += 1; if (*p=='\\') { p += 1; if ((i = digit(*p))>=0 && i<=7) { n = i; p += 1; if ((i = digit(*p))>=0 && i<=7) { p += 1; n <<= 3; n += i; if ((i = digit(*p))>=0 && i<=7) { p += 1; n <<= 3; n += i; } } } else if (*p=='x') { p += 1; while ((i = digit(*p))>=0 && i<=15) { p += 1; n <<= 4; n += i; } } else { static char cvcon[] = "b\bf\fn\nr\rt\tv\v''\"\"??\\\\"; for (i=0; i=sizeof(cvcon)) error(WARNING, "Undefined escape in character constant"); } } else if (*p=='\'') error(ERROR, "Empty character constant"); else n = *p++; if (*p!='\'') error(WARNING, "Multibyte character constant undefined"); else if (n>127) error(WARNING, "Character constant taken as not signed"); v.val = n; break; case STRING: error(ERROR, "String in #if/#elsif"); break; } return v; } int digit(int i) { if ('0'<=i && i<='9') i -= '0'; else if ('a'<=i && i<='f') i -= 'a'-10; else if ('A'<=i && i<='F') i -= 'A'-10; else i = -1; return i; } openarena_0.8.8.orig/code/tools/lcc/cpp/macro.c0000644000175000017500000002467411656310262020103 0ustar smcvsmcv#include #include #include #include "cpp.h" /* * do a macro definition. tp points to the name being defined in the line */ void dodefine(Tokenrow *trp) { Token *tp; Nlist *np; Tokenrow *def, *args; tp = trp->tp+1; if (tp>=trp->lp || tp->type!=NAME) { error(ERROR, "#defined token is not a name"); return; } np = lookup(tp, 1); if (np->flag&ISUNCHANGE) { error(ERROR, "#defined token %t can't be redefined", tp); return; } /* collect arguments */ tp += 1; args = NULL; if (tplp && tp->type==LP && tp->wslen==0) { /* macro with args */ int narg = 0; tp += 1; args = new(Tokenrow); maketokenrow(2, args); if (tp->type!=RP) { int err = 0; for (;;) { Token *atp; if (tp->type!=NAME) { err++; break; } if (narg>=args->max) growtokenrow(args); for (atp=args->bp; atplp; atp++) if (atp->len==tp->len && strncmp((char*)atp->t, (char*)tp->t, tp->len)==0) error(ERROR, "Duplicate macro argument"); *args->lp++ = *tp; narg++; tp += 1; if (tp->type==RP) break; if (tp->type!=COMMA) { err++; break; } tp += 1; } if (err) { error(ERROR, "Syntax error in macro parameters"); return; } } tp += 1; } trp->tp = tp; if (((trp->lp)-1)->type==NL) trp->lp -= 1; def = normtokenrow(trp); if (np->flag&ISDEFINED) { if (comparetokens(def, np->vp) || (np->ap==NULL) != (args==NULL) || (np->ap && comparetokens(args, np->ap))) error(ERROR, "Macro redefinition of %t", trp->bp+2); } if (args) { Tokenrow *tap; tap = normtokenrow(args); dofree(args->bp); args = tap; } np->ap = args; np->vp = def; np->flag |= ISDEFINED; } /* * Definition received via -D or -U */ void doadefine(Tokenrow *trp, int type) { Nlist *np; static Token onetoken[1] = {{ NUMBER, 0, 0, 0, 1, (uchar*)"1" }}; static Tokenrow onetr = { onetoken, onetoken, onetoken+1, 1 }; trp->tp = trp->bp; if (type=='U') { if (trp->lp-trp->tp != 2 || trp->tp->type!=NAME) goto syntax; if ((np = lookup(trp->tp, 0)) == NULL) return; np->flag &= ~ISDEFINED; return; } if (trp->tp >= trp->lp || trp->tp->type!=NAME) goto syntax; np = lookup(trp->tp, 1); np->flag |= ISDEFINED; trp->tp += 1; if (trp->tp >= trp->lp || trp->tp->type==END) { np->vp = &onetr; return; } if (trp->tp->type!=ASGN) goto syntax; trp->tp += 1; if ((trp->lp-1)->type == END) trp->lp -= 1; np->vp = normtokenrow(trp); return; syntax: error(FATAL, "Illegal -D or -U argument %r", trp); } /* * Do macro expansion in a row of tokens. * Flag is NULL if more input can be gathered. */ void expandrow(Tokenrow *trp, char *flag) { Token *tp; Nlist *np; if (flag) setsource(flag, -1, ""); for (tp = trp->tp; tplp; ) { if (tp->type!=NAME || quicklook(tp->t[0], tp->len>1?tp->t[1]:0)==0 || (np = lookup(tp, 0))==NULL || (np->flag&(ISDEFINED|ISMAC))==0 || (tp->hideset && checkhideset(tp->hideset, np))) { tp++; continue; } trp->tp = tp; if (np->val==KDEFINED) { tp->type = DEFINED; if ((tp+1)lp && (tp+1)->type==NAME) (tp+1)->type = NAME1; else if ((tp+3)lp && (tp+1)->type==LP && (tp+2)->type==NAME && (tp+3)->type==RP) (tp+2)->type = NAME1; else error(ERROR, "Incorrect syntax for `defined'"); tp++; continue; } if (np->flag&ISMAC) builtin(trp, np->val); else { expand(trp, np); } tp = trp->tp; } if (flag) unsetsource(); } /* * Expand the macro whose name is np, at token trp->tp, in the tokenrow. * Return trp->tp at the first token next to be expanded * (ordinarily the beginning of the expansion) */ void expand(Tokenrow *trp, Nlist *np) { Tokenrow ntr; int ntokc, narg, i; Token *tp; Tokenrow *atr[NARG+1]; int hs; copytokenrow(&ntr, np->vp); /* copy macro value */ if (np->ap==NULL) /* parameterless */ ntokc = 1; else { ntokc = gatherargs(trp, atr, &narg); if (narg<0) { /* not actually a call (no '(') */ trp->tp++; return; } if (narg != rowlen(np->ap)) { error(ERROR, "Disagreement in number of macro arguments"); trp->tp->hideset = newhideset(trp->tp->hideset, np); trp->tp += ntokc; return; } substargs(np, &ntr, atr); /* put args into replacement */ for (i=0; ibp); dofree(atr[i]); } } doconcat(&ntr); /* execute ## operators */ hs = newhideset(trp->tp->hideset, np); for (tp=ntr.bp; tptype==NAME) { if (tp->hideset==0) tp->hideset = hs; else tp->hideset = unionhideset(tp->hideset, hs); } } ntr.tp = ntr.bp; insertrow(trp, ntokc, &ntr); trp->tp -= rowlen(&ntr); dofree(ntr.bp); return; } /* * Gather an arglist, starting in trp with tp pointing at the macro name. * Return total number of tokens passed, stash number of args found. * trp->tp is not changed relative to the tokenrow. */ int gatherargs(Tokenrow *trp, Tokenrow **atr, int *narg) { int parens = 1; int ntok = 0; Token *bp, *lp; Tokenrow ttr; int ntokp; int needspace; *narg = -1; /* means that there is no macro call */ /* look for the ( */ for (;;) { trp->tp++; ntok++; if (trp->tp >= trp->lp) { gettokens(trp, 0); if ((trp->lp-1)->type==END) { trp->lp -= 1; trp->tp -= ntok; return ntok; } } if (trp->tp->type==LP) break; if (trp->tp->type!=NL) return ntok; } *narg = 0; ntok++; ntokp = ntok; trp->tp++; /* search for the terminating ), possibly extending the row */ needspace = 0; while (parens>0) { if (trp->tp >= trp->lp) gettokens(trp, 0); if (needspace) { needspace = 0; makespace(trp); } if (trp->tp->type==END) { trp->lp -= 1; trp->tp -= ntok; error(ERROR, "EOF in macro arglist"); return ntok; } if (trp->tp->type==NL) { trp->tp += 1; adjustrow(trp, -1); trp->tp -= 1; makespace(trp); needspace = 1; continue; } if (trp->tp->type==LP) parens++; else if (trp->tp->type==RP) parens--; trp->tp++; ntok++; } trp->tp -= ntok; /* Now trp->tp won't move underneath us */ lp = bp = trp->tp+ntokp; for (; parens>=0; lp++) { if (lp->type == LP) { parens++; continue; } if (lp->type==RP) parens--; if (lp->type==DSHARP) lp->type = DSHARP1; /* ## not special in arg */ if ((lp->type==COMMA && parens==0) || (parens<0 && (lp-1)->type!=LP)) { if (*narg>=NARG-1) error(FATAL, "Sorry, too many macro arguments"); ttr.bp = ttr.tp = bp; ttr.lp = lp; atr[(*narg)++] = normtokenrow(&ttr); bp = lp+1; } } return ntok; } /* * substitute the argument list into the replacement string * This would be simple except for ## and # */ void substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) { Tokenrow tatr; Token *tp; int ntok, argno; for (rtr->tp=rtr->bp; rtr->tplp; ) { if (rtr->tp->type==SHARP) { /* string operator */ tp = rtr->tp; rtr->tp += 1; if ((argno = lookuparg(np, rtr->tp))<0) { error(ERROR, "# not followed by macro parameter"); continue; } ntok = 1 + (rtr->tp - tp); rtr->tp = tp; insertrow(rtr, ntok, stringify(atr[argno])); continue; } if (rtr->tp->type==NAME && (argno = lookuparg(np, rtr->tp)) >= 0) { if ((rtr->tp+1)->type==DSHARP || (rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP)) insertrow(rtr, 1, atr[argno]); else { copytokenrow(&tatr, atr[argno]); expandrow(&tatr, ""); insertrow(rtr, 1, &tatr); dofree(tatr.bp); } continue; } rtr->tp++; } } /* * Evaluate the ## operators in a tokenrow */ void doconcat(Tokenrow *trp) { Token *ltp, *ntp; Tokenrow ntr; int len; for (trp->tp=trp->bp; trp->tplp; trp->tp++) { if (trp->tp->type==DSHARP1) trp->tp->type = DSHARP; else if (trp->tp->type==DSHARP) { char tt[128]; ltp = trp->tp-1; ntp = trp->tp+1; if (ltpbp || ntp>=trp->lp) { error(ERROR, "## occurs at border of replacement"); continue; } len = ltp->len + ntp->len; strncpy((char*)tt, (char*)ltp->t, ltp->len); strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); tt[len] = '\0'; setsource("<##>", -1, tt); maketokenrow(3, &ntr); gettokens(&ntr, 1); unsetsource(); if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) error(WARNING, "Bad token %r produced by ##", &ntr); ntr.lp = ntr.bp+1; trp->tp = ltp; makespace(&ntr); insertrow(trp, (ntp-ltp)+1, &ntr); dofree(ntr.bp); trp->tp--; } } } /* * tp is a potential parameter name of macro mac; * look it up in mac's arglist, and if found, return the * corresponding index in the argname array. Return -1 if not found. */ int lookuparg(Nlist *mac, Token *tp) { Token *ap; if (tp->type!=NAME || mac->ap==NULL) return -1; for (ap=mac->ap->bp; apap->lp; ap++) { if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) return ap - mac->ap->bp; } return -1; } /* * Return a quoted version of the tokenrow (from # arg) */ #define STRLEN 512 Tokenrow * stringify(Tokenrow *vp) { static Token t = { STRING }; static Tokenrow tr = { &t, &t, &t+1, 1 }; Token *tp; uchar s[STRLEN]; uchar *sp = s, *cp; int i, instring; *sp++ = '"'; for (tp = vp->bp; tp < vp->lp; tp++) { instring = tp->type==STRING || tp->type==CCON; if (sp+2*tp->len >= &s[STRLEN-10]) { error(ERROR, "Stringified macro arg is too long"); break; } if (tp->wslen && (tp->flag&XPWS)==0) *sp++ = ' '; for (i=0, cp=tp->t; ilen; i++) { if (instring && (*cp=='"' || *cp=='\\')) *sp++ = '\\'; *sp++ = *cp++; } } *sp++ = '"'; *sp = '\0'; sp = s; t.len = strlen((char*)sp); t.t = newstring(sp, t.len, 0); return &tr; } /* * expand a builtin name */ void builtin(Tokenrow *trp, int biname) { char *op; Token *tp; Source *s; tp = trp->tp; trp->tp++; /* need to find the real source */ s = cursource; while (s && s->fd==-1) s = s->next; if (s==NULL) s = cursource; /* most are strings */ tp->type = STRING; if (tp->wslen) { *outp++ = ' '; tp->wslen = 1; } op = outp; *op++ = '"'; switch (biname) { case KLINENO: tp->type = NUMBER; op = outnum(op-1, s->line); break; case KFILE: { char *src = s->filename; while ((*op++ = *src++) != 0) if (src[-1] == '\\') *op++ = '\\'; op--; break; } case KDATE: strncpy(op, curtime+4, 7); strncpy(op+7, curtime+20, 4); op += 11; break; case KTIME: strncpy(op, curtime+11, 8); op += 8; break; default: error(ERROR, "cpp botch: unknown internal macro"); return; } if (tp->type==STRING) *op++ = '"'; tp->t = (uchar*)outp; tp->len = op - outp; outp = op; } openarena_0.8.8.orig/code/tools/lcc/cpp/tokens.c0000644000175000017500000001522411656310262020274 0ustar smcvsmcv#include #include #include #include "cpp.h" static char wbuf[2*OBS]; static char *wbp = wbuf; /* * 1 for tokens that don't need whitespace when they get inserted * by macro expansion */ static const char wstab[] = { 0, /* END */ 0, /* UNCLASS */ 0, /* NAME */ 0, /* NUMBER */ 0, /* STRING */ 0, /* CCON */ 1, /* NL */ 0, /* WS */ 0, /* DSHARP */ 0, /* EQ */ 0, /* NEQ */ 0, /* LEQ */ 0, /* GEQ */ 0, /* LSH */ 0, /* RSH */ 0, /* LAND */ 0, /* LOR */ 0, /* PPLUS */ 0, /* MMINUS */ 0, /* ARROW */ 1, /* SBRA */ 1, /* SKET */ 1, /* LP */ 1, /* RP */ 0, /* DOT */ 0, /* AND */ 0, /* STAR */ 0, /* PLUS */ 0, /* MINUS */ 0, /* TILDE */ 0, /* NOT */ 0, /* SLASH */ 0, /* PCT */ 0, /* LT */ 0, /* GT */ 0, /* CIRC */ 0, /* OR */ 0, /* QUEST */ 0, /* COLON */ 0, /* ASGN */ 1, /* COMMA */ 0, /* SHARP */ 1, /* SEMIC */ 1, /* CBRA */ 1, /* CKET */ 0, /* ASPLUS */ 0, /* ASMINUS */ 0, /* ASSTAR */ 0, /* ASSLASH */ 0, /* ASPCT */ 0, /* ASCIRC */ 0, /* ASLSH */ 0, /* ASRSH */ 0, /* ASOR */ 0, /* ASAND */ 0, /* ELLIPS */ 0, /* DSHARP1 */ 0, /* NAME1 */ 0, /* DEFINED */ 0, /* UMINUS */ }; void maketokenrow(int size, Tokenrow *trp) { trp->max = size; if (size>0) trp->bp = (Token *)domalloc(size*sizeof(Token)); else trp->bp = NULL; trp->tp = trp->bp; trp->lp = trp->bp; } Token * growtokenrow(Tokenrow *trp) { int ncur = trp->tp - trp->bp; int nlast = trp->lp - trp->bp; trp->max = 3*trp->max/2 + 1; trp->bp = (Token *)realloc(trp->bp, trp->max*sizeof(Token)); if (trp->bp == NULL) error(FATAL, "Out of memory from realloc"); trp->lp = &trp->bp[nlast]; trp->tp = &trp->bp[ncur]; return trp->lp; } /* * Compare a row of tokens, ignoring the content of WS; return !=0 if different */ int comparetokens(Tokenrow *tr1, Tokenrow *tr2) { Token *tp1, *tp2; tp1 = tr1->tp; tp2 = tr2->tp; if (tr1->lp-tp1 != tr2->lp-tp2) return 1; for (; tp1lp ; tp1++, tp2++) { if (tp1->type != tp2->type || (tp1->wslen==0) != (tp2->wslen==0) || tp1->len != tp2->len || strncmp((char*)tp1->t, (char*)tp2->t, tp1->len)!=0) return 1; } return 0; } /* * replace ntok tokens starting at dtr->tp with the contents of str. * tp ends up pointing just beyond the replacement. * Canonical whitespace is assured on each side. */ void insertrow(Tokenrow *dtr, int ntok, Tokenrow *str) { int nrtok = rowlen(str); dtr->tp += ntok; adjustrow(dtr, nrtok-ntok); dtr->tp -= ntok; movetokenrow(dtr, str); makespace(dtr); dtr->tp += nrtok; makespace(dtr); } /* * make sure there is WS before trp->tp, if tokens might merge in the output */ void makespace(Tokenrow *trp) { uchar *tt; Token *tp = trp->tp; if (tp >= trp->lp) return; if (tp->wslen) { if (tp->flag&XPWS && (wstab[tp->type] || (trp->tp>trp->bp && wstab[(tp-1)->type]))) { tp->wslen = 0; return; } tp->t[-1] = ' '; return; } if (wstab[tp->type] || (trp->tp>trp->bp && wstab[(tp-1)->type])) return; tt = newstring(tp->t, tp->len, 1); *tt++ = ' '; tp->t = tt; tp->wslen = 1; tp->flag |= XPWS; } /* * Copy an entire tokenrow into another, at tp. * It is assumed that there is enough space. * Not strictly conforming. */ void movetokenrow(Tokenrow *dtr, Tokenrow *str) { int nby; /* nby = sizeof(Token) * (str->lp - str->bp); */ nby = (char *)str->lp - (char *)str->bp; memmove(dtr->tp, str->bp, nby); } /* * Move the tokens in a row, starting at tr->tp, rightward by nt tokens; * nt may be negative (left move). * The row may need to be grown. * Non-strictly conforming because of the (char *), but easily fixed */ void adjustrow(Tokenrow *trp, int nt) { int nby, size; if (nt==0) return; size = (trp->lp - trp->bp) + nt; while (size > trp->max) growtokenrow(trp); /* nby = sizeof(Token) * (trp->lp - trp->tp); */ nby = (char *)trp->lp - (char *)trp->tp; if (nby) memmove(trp->tp+nt, trp->tp, nby); trp->lp += nt; } /* * Copy a row of tokens into the destination holder, allocating * the space for the contents. Return the destination. */ Tokenrow * copytokenrow(Tokenrow *dtr, Tokenrow *str) { int len = rowlen(str); maketokenrow(len, dtr); movetokenrow(dtr, str); dtr->lp += len; return dtr; } /* * Produce a copy of a row of tokens. Start at trp->tp. * The value strings are copied as well. The first token * has WS available. */ Tokenrow * normtokenrow(Tokenrow *trp) { Token *tp; Tokenrow *ntrp = new(Tokenrow); int len; len = trp->lp - trp->tp; if (len<=0) len = 1; maketokenrow(len, ntrp); for (tp=trp->tp; tp < trp->lp; tp++) { *ntrp->lp = *tp; if (tp->len) { ntrp->lp->t = newstring(tp->t, tp->len, 1); *ntrp->lp->t++ = ' '; if (tp->wslen) ntrp->lp->wslen = 1; } ntrp->lp++; } if (ntrp->lp > ntrp->bp) ntrp->bp->wslen = 0; return ntrp; } /* * Debugging */ void peektokens(Tokenrow *trp, char *str) { Token *tp; tp = trp->tp; flushout(); if (str) fprintf(stderr, "%s ", str); if (tpbp || tp>trp->lp) fprintf(stderr, "(tp offset %d) ", tp-trp->bp); for (tp=trp->bp; tplp && tpbp+32; tp++) { if (tp->type!=NL) { int c = tp->t[tp->len]; tp->t[tp->len] = 0; fprintf(stderr, "%s", tp->t); tp->t[tp->len] = c; } if (tp->type==NAME) { fprintf(stderr, tp==trp->tp?"{*":"{"); prhideset(tp->hideset); fprintf(stderr, "} "); } else fprintf(stderr, tp==trp->tp?"{%x*} ":"{%x} ", tp->type); } fprintf(stderr, "\n"); fflush(stderr); } void puttokens(Tokenrow *trp) { Token *tp; int len; uchar *p; if (verbose) peektokens(trp, ""); tp = trp->bp; for (; tplp; tp++) { len = tp->len+tp->wslen; p = tp->t-tp->wslen; while (tplp-1 && p+len == (tp+1)->t - (tp+1)->wslen) { tp++; len += tp->wslen+tp->len; } if (len>OBS/2) { /* handle giant token */ if (wbp > wbuf) write(1, wbuf, wbp-wbuf); write(1, (char *)p, len); wbp = wbuf; } else { memcpy(wbp, p, len); wbp += len; } if (wbp >= &wbuf[OBS]) { write(1, wbuf, OBS); if (wbp > &wbuf[OBS]) memcpy(wbuf, wbuf+OBS, wbp - &wbuf[OBS]); wbp -= OBS; } } trp->tp = tp; if (cursource->fd==0) flushout(); } void flushout(void) { if (wbp>wbuf) { write(1, wbuf, wbp-wbuf); wbp = wbuf; } } /* * turn a row into just a newline */ void setempty(Tokenrow *trp) { trp->tp = trp->bp; trp->lp = trp->bp+1; *trp->bp = nltoken; } /* * generate a number */ char * outnum(char *p, int n) { if (n>=10) p = outnum(p, n/10); *p++ = n%10 + '0'; return p; } /* * allocate and initialize a new string from s, of length l, at offset o * Null terminated. */ uchar * newstring(uchar *s, int l, int o) { uchar *ns = (uchar *)domalloc(l+o+1); ns[l+o] = '\0'; return (uchar*)strncpy((char*)ns+o, (char*)s, l) - o; } openarena_0.8.8.orig/code/tools/lcc/cpp/nlist.c0000644000175000017500000000401311656310262020114 0ustar smcvsmcv#include #include #include #include "cpp.h" extern char *optarg; extern int optind; extern int verbose; extern int Cplusplus; Nlist *kwdefined; char wd[128]; #define NLSIZE 128 static Nlist *nlist[NLSIZE]; struct kwtab { char *kw; int val; int flag; } kwtab[] = { {"if", KIF, ISKW}, {"ifdef", KIFDEF, ISKW}, {"ifndef", KIFNDEF, ISKW}, {"elif", KELIF, ISKW}, {"else", KELSE, ISKW}, {"endif", KENDIF, ISKW}, {"include", KINCLUDE, ISKW}, {"define", KDEFINE, ISKW}, {"undef", KUNDEF, ISKW}, {"line", KLINE, ISKW}, {"warning", KWARNING, ISKW}, {"error", KERROR, ISKW}, {"pragma", KPRAGMA, ISKW}, {"eval", KEVAL, ISKW}, {"defined", KDEFINED, ISDEFINED+ISUNCHANGE}, {"__LINE__", KLINENO, ISMAC+ISUNCHANGE}, {"__FILE__", KFILE, ISMAC+ISUNCHANGE}, {"__DATE__", KDATE, ISMAC+ISUNCHANGE}, {"__TIME__", KTIME, ISMAC+ISUNCHANGE}, {"__STDC__", KSTDC, ISUNCHANGE}, {NULL} }; unsigned long namebit[077+1]; Nlist *np; void setup_kwtab(void) { struct kwtab *kp; Nlist *np; Token t; static Token deftoken[1] = {{ NAME, 0, 0, 0, 7, (uchar*)"defined" }}; static Tokenrow deftr = { deftoken, deftoken, deftoken+1, 1 }; for (kp=kwtab; kp->kw; kp++) { t.t = (uchar*)kp->kw; t.len = strlen(kp->kw); np = lookup(&t, 1); np->flag = kp->flag; np->val = kp->val; if (np->val == KDEFINED) { kwdefined = np; np->val = NAME; np->vp = &deftr; np->ap = 0; } } } Nlist * lookup(Token *tp, int install) { unsigned int h; Nlist *np; uchar *cp, *cpe; h = 0; for (cp=tp->t, cpe=cp+tp->len; cpt==*np->name && tp->len==np->len && strncmp((char*)tp->t, (char*)np->name, tp->len)==0) return np; np = np->next; } if (install) { np = new(Nlist); np->vp = NULL; np->ap = NULL; np->flag = 0; np->val = 0; np->len = tp->len; np->name = newstring(tp->t, tp->len, 0); np->next = nlist[h]; nlist[h] = np; quickset(tp->t[0], tp->len>1? tp->t[1]:0); return np; } return NULL; } openarena_0.8.8.orig/code/tools/lcc/cpp/hideset.c0000644000175000017500000000373311656310262020420 0ustar smcvsmcv#include #include #include #include "cpp.h" /* * A hideset is a null-terminated array of Nlist pointers. * They are referred to by indices in the hidesets array. * Hideset 0 is empty. */ #define HSSIZ 32 typedef Nlist **Hideset; Hideset *hidesets; int nhidesets = 0; int maxhidesets = 3; int inserths(Hideset, Hideset, Nlist *); /* * Test for membership in a hideset */ int checkhideset(int hs, Nlist *np) { Hideset hsp; if (hs>=nhidesets) abort(); for (hsp = hidesets[hs]; *hsp; hsp++) { if (*hsp == np) return 1; } return 0; } /* * Return the (possibly new) hideset obtained by adding np to hs. */ int newhideset(int hs, Nlist *np) { int i, len; Nlist *nhs[HSSIZ+3]; Hideset hs1, hs2; len = inserths(nhs, hidesets[hs], np); for (i=0; i=HSSIZ) return hs; if (nhidesets >= maxhidesets) { maxhidesets = 3*maxhidesets/2+1; hidesets = (Hideset *)realloc(hidesets, (sizeof (Hideset *))*maxhidesets); if (hidesets == NULL) error(FATAL, "Out of memory from realloc"); } hs1 = (Hideset)domalloc(len*sizeof(Hideset)); memmove(hs1, nhs, len*sizeof(Hideset)); hidesets[nhidesets] = hs1; return nhidesets++; } int inserths(Hideset dhs, Hideset shs, Nlist *np) { Hideset odhs = dhs; while (*shs && *shs < np) *dhs++ = *shs++; if (*shs != np) *dhs++ = np; do { *dhs++ = *shs; } while (*shs++); return dhs - odhs; } /* * Hideset union */ int unionhideset(int hs1, int hs2) { Hideset hp; for (hp = hidesets[hs2]; *hp; hp++) hs1 = newhideset(hs1, *hp); return hs1; } void iniths(void) { hidesets = (Hideset *)domalloc(maxhidesets*sizeof(Hideset *)); hidesets[0] = (Hideset)domalloc(sizeof(Hideset)); *hidesets[0] = NULL; nhidesets++; } void prhideset(int hs) { Hideset np; for (np = hidesets[hs]; *np; np++) { fprintf(stderr, (char*)(*np)->name, (*np)->len); fprintf(stderr, " "); } } openarena_0.8.8.orig/code/tools/lcc/cpp/getopt.c0000644000175000017500000000203711656310262020271 0ustar smcvsmcv#include #include #define EPR fprintf(stderr, #define ERR(str, chr) if(opterr){EPR "%s%c\n", str, chr);} int opterr = 1; int optind = 1; int optopt; char *optarg; int lcc_getopt (int argc, char *const argv[], const char *opts) { static int sp = 1; int c; char *cp; if (sp == 1) { if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return -1; else if (strcmp(argv[optind], "--") == 0) { optind++; return -1; } } optopt = c = argv[optind][sp]; if (c == ':' || (cp=strchr(opts, c)) == 0) { ERR (": illegal option -- ", c); if (argv[optind][++sp] == '\0') { optind++; sp = 1; } return '?'; } if (*++cp == ':') { if (argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if (++optind >= argc) { ERR (": option requires an argument -- ", c); sp = 1; return '?'; } else optarg = argv[optind++]; sp = 1; } else { if (argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = 0; } return c; } openarena_0.8.8.orig/code/tools/lcc/cpp/cpp.c0000644000175000017500000001355211656310262017555 0ustar smcvsmcv#include #include #include #include #include #include "cpp.h" char rcsid[] = "cpp.c - faked rcsid"; #define OUTS 16384 char outbuf[OUTS]; char *outp = outbuf; Source *cursource; int nerrs; struct token nltoken = { NL, 0, 0, 0, 1, (uchar*)"\n" }; char *curtime; int incdepth; int ifdepth; int ifsatisfied[NIF]; int skipping; int main(int argc, char **argv) { Tokenrow tr; time_t t; char ebuf[BUFSIZ]; setbuf(stderr, ebuf); t = time(NULL); curtime = ctime(&t); maketokenrow(3, &tr); expandlex(); setup(argc, argv); fixlex(); iniths(); genline(); process(&tr); flushout(); fflush(stderr); exit(nerrs > 0); return 0; } void process(Tokenrow *trp) { int anymacros = 0; for (;;) { if (trp->tp >= trp->lp) { trp->tp = trp->lp = trp->bp; outp = outbuf; anymacros |= gettokens(trp, 1); trp->tp = trp->bp; } if (trp->tp->type == END) { if (--incdepth>=0) { if (cursource->ifdepth) error(ERROR, "Unterminated conditional in #include"); unsetsource(); cursource->line += cursource->lineinc; trp->tp = trp->lp; genline(); continue; } if (ifdepth) error(ERROR, "Unterminated #if/#ifdef/#ifndef"); break; } if (trp->tp->type==SHARP) { trp->tp += 1; control(trp); } else if (!skipping && anymacros) expandrow(trp, NULL); if (skipping) setempty(trp); puttokens(trp); anymacros = 0; cursource->line += cursource->lineinc; if (cursource->lineinc>1) { genline(); } } } void control(Tokenrow *trp) { Nlist *np; Token *tp; tp = trp->tp; if (tp->type!=NAME) { if (tp->type==NUMBER) goto kline; if (tp->type != NL) error(ERROR, "Unidentifiable control line"); return; /* else empty line */ } if ((np = lookup(tp, 0))==NULL || ((np->flag&ISKW)==0 && !skipping)) { error(WARNING, "Unknown preprocessor control %t", tp); return; } if (skipping) { switch (np->val) { case KENDIF: if (--ifdepthifdepth; setempty(trp); return; case KIFDEF: case KIFNDEF: case KIF: if (++ifdepth >= NIF) error(FATAL, "#if too deeply nested"); ++cursource->ifdepth; return; case KELIF: case KELSE: if (ifdepth<=skipping) break; return; default: return; } } switch (np->val) { case KDEFINE: dodefine(trp); break; case KUNDEF: tp += 1; if (tp->type!=NAME || trp->lp - trp->bp != 4) { error(ERROR, "Syntax error in #undef"); break; } if ((np = lookup(tp, 0)) != NULL) np->flag &= ~ISDEFINED; break; case KPRAGMA: return; case KIFDEF: case KIFNDEF: case KIF: if (++ifdepth >= NIF) error(FATAL, "#if too deeply nested"); ++cursource->ifdepth; ifsatisfied[ifdepth] = 0; if (eval(trp, np->val)) ifsatisfied[ifdepth] = 1; else skipping = ifdepth; break; case KELIF: if (ifdepth==0) { error(ERROR, "#elif with no #if"); return; } if (ifsatisfied[ifdepth]==2) error(ERROR, "#elif after #else"); if (eval(trp, np->val)) { if (ifsatisfied[ifdepth]) skipping = ifdepth; else { skipping = 0; ifsatisfied[ifdepth] = 1; } } else skipping = ifdepth; break; case KELSE: if (ifdepth==0 || cursource->ifdepth==0) { error(ERROR, "#else with no #if"); return; } if (ifsatisfied[ifdepth]==2) error(ERROR, "#else after #else"); if (trp->lp - trp->bp != 3) error(ERROR, "Syntax error in #else"); skipping = ifsatisfied[ifdepth]? ifdepth: 0; ifsatisfied[ifdepth] = 2; break; case KENDIF: if (ifdepth==0 || cursource->ifdepth==0) { error(ERROR, "#endif with no #if"); return; } --ifdepth; --cursource->ifdepth; if (trp->lp - trp->bp != 3) error(WARNING, "Syntax error in #endif"); break; case KWARNING: trp->tp = tp+1; error(WARNING, "#warning directive: %r", trp); break; case KERROR: trp->tp = tp+1; error(ERROR, "#error directive: %r", trp); break; case KLINE: trp->tp = tp+1; expandrow(trp, ""); tp = trp->bp+2; kline: if (tp+1>=trp->lp || tp->type!=NUMBER || tp+3lp || ((tp+3==trp->lp && ((tp+1)->type!=STRING))||*(tp+1)->t=='L')){ error(ERROR, "Syntax error in #line"); return; } cursource->line = atol((char*)tp->t)-1; if (cursource->line<0 || cursource->line>=32768) error(WARNING, "#line specifies number out of range"); tp = tp+1; if (tp+1lp) cursource->filename=(char*)newstring(tp->t+1,tp->len-2,0); return; case KDEFINED: error(ERROR, "Bad syntax for control line"); break; case KINCLUDE: doinclude(trp); trp->lp = trp->bp; return; case KEVAL: eval(trp, np->val); break; default: error(ERROR, "Preprocessor control `%t' not yet implemented", tp); break; } setempty(trp); return; } void * domalloc(int size) { void *p = malloc(size); if (p==NULL) error(FATAL, "Out of memory from malloc"); return p; } void dofree(void *p) { free(p); } void error(enum errtype type, char *string, ...) { va_list ap; char *cp, *ep; Token *tp; Tokenrow *trp; Source *s; int i; fprintf(stderr, "cpp: "); for (s=cursource; s; s=s->next) if (*s->filename) fprintf(stderr, "%s:%d ", s->filename, s->line); va_start(ap, string); for (ep=string; *ep; ep++) { if (*ep=='%') { switch (*++ep) { case 's': cp = va_arg(ap, char *); fprintf(stderr, "%s", cp); break; case 'd': i = va_arg(ap, int); fprintf(stderr, "%d", i); break; case 't': tp = va_arg(ap, Token *); fprintf(stderr, "%.*s", tp->len, tp->t); break; case 'r': trp = va_arg(ap, Tokenrow *); for (tp=trp->tp; tplp&&tp->type!=NL; tp++) { if (tp>trp->tp && tp->wslen) fputc(' ', stderr); fprintf(stderr, "%.*s", tp->len, tp->t); } break; default: fputc(*ep, stderr); break; } } else fputc(*ep, stderr); } va_end(ap); fputc('\n', stderr); if (type==FATAL) exit(1); if (type!=WARNING) nerrs = 1; fflush(stderr); } openarena_0.8.8.orig/code/tools/lcc/cpp/cpp.h0000644000175000017500000001120111656310262017547 0ustar smcvsmcv#define INS 32768 /* input buffer */ #define OBS 4096 /* outbut buffer */ #define NARG 32 /* Max number arguments to a macro */ #define NINCLUDE 32 /* Max number of include directories (-I) */ #define NIF 32 /* depth of nesting of #if */ #ifndef EOF #define EOF (-1) #endif #ifndef NULL #define NULL 0 #endif #ifndef __alpha typedef unsigned char uchar; #endif enum toktype { END, UNCLASS, NAME, NUMBER, STRING, CCON, NL, WS, DSHARP, EQ, NEQ, LEQ, GEQ, LSH, RSH, LAND, LOR, PPLUS, MMINUS, ARROW, SBRA, SKET, LP, RP, DOT, AND, STAR, PLUS, MINUS, TILDE, NOT, SLASH, PCT, LT, GT, CIRC, OR, QUEST, COLON, ASGN, COMMA, SHARP, SEMIC, CBRA, CKET, ASPLUS, ASMINUS, ASSTAR, ASSLASH, ASPCT, ASCIRC, ASLSH, ASRSH, ASOR, ASAND, ELLIPS, DSHARP1, NAME1, DEFINED, UMINUS }; enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE, KDEFINE, KUNDEF, KLINE, KWARNING, KERROR, KPRAGMA, KDEFINED, KLINENO, KFILE, KDATE, KTIME, KSTDC, KEVAL }; #define ISDEFINED 01 /* has #defined value */ #define ISKW 02 /* is PP keyword */ #define ISUNCHANGE 04 /* can't be #defined in PP */ #define ISMAC 010 /* builtin macro, e.g. __LINE__ */ #define EOB 0xFE /* sentinel for end of input buffer */ #define EOFC 0xFD /* sentinel for end of input file */ #define XPWS 1 /* token flag: white space to assure token sep. */ typedef struct token { unsigned char type; unsigned char flag; unsigned short hideset; unsigned int wslen; unsigned int len; uchar *t; } Token; typedef struct tokenrow { Token *tp; /* current one to scan */ Token *bp; /* base (allocated value) */ Token *lp; /* last+1 token used */ int max; /* number allocated */ } Tokenrow; typedef struct source { char *filename; /* name of file of the source */ int line; /* current line number */ int lineinc; /* adjustment for \\n lines */ uchar *inb; /* input buffer */ uchar *inp; /* input pointer */ uchar *inl; /* end of input */ int fd; /* input source */ int ifdepth; /* conditional nesting in include */ struct source *next; /* stack for #include */ } Source; typedef struct nlist { struct nlist *next; uchar *name; int len; Tokenrow *vp; /* value as macro */ Tokenrow *ap; /* list of argument names, if any */ char val; /* value as preprocessor name */ char flag; /* is defined, is pp name */ } Nlist; typedef struct includelist { char deleted; char always; char *file; } Includelist; #define new(t) (t *)domalloc(sizeof(t)) #define quicklook(a,b) (namebit[(a)&077] & (1<<((b)&037))) #define quickset(a,b) namebit[(a)&077] |= (1<<((b)&037)) extern unsigned long namebit[077+1]; enum errtype { WARNING, ERROR, FATAL }; void expandlex(void); void fixlex(void); void setup(int, char **); int gettokens(Tokenrow *, int); int comparetokens(Tokenrow *, Tokenrow *); Source *setsource(char *, int, char *); void unsetsource(void); void puttokens(Tokenrow *); void process(Tokenrow *); void *domalloc(int); void dofree(void *); void error(enum errtype, char *, ...); void flushout(void); int fillbuf(Source *); int trigraph(Source *); int foldline(Source *); Nlist *lookup(Token *, int); void control(Tokenrow *); void dodefine(Tokenrow *); void doadefine(Tokenrow *, int); void doinclude(Tokenrow *); void appendDirToIncludeList( char *dir ); void doif(Tokenrow *, enum kwtype); void expand(Tokenrow *, Nlist *); void builtin(Tokenrow *, int); int gatherargs(Tokenrow *, Tokenrow **, int *); void substargs(Nlist *, Tokenrow *, Tokenrow **); void expandrow(Tokenrow *, char *); void maketokenrow(int, Tokenrow *); Tokenrow *copytokenrow(Tokenrow *, Tokenrow *); Token *growtokenrow(Tokenrow *); Tokenrow *normtokenrow(Tokenrow *); void adjustrow(Tokenrow *, int); void movetokenrow(Tokenrow *, Tokenrow *); void insertrow(Tokenrow *, int, Tokenrow *); void peektokens(Tokenrow *, char *); void doconcat(Tokenrow *); Tokenrow *stringify(Tokenrow *); int lookuparg(Nlist *, Token *); long eval(Tokenrow *, int); void genline(void); void setempty(Tokenrow *); void makespace(Tokenrow *); char *outnum(char *, int); int digit(int); uchar *newstring(uchar *, int, int); int checkhideset(int, Nlist *); void prhideset(int); int newhideset(int, Nlist *); int unionhideset(int, int); void iniths(void); void setobjname(char *); #define rowlen(tokrow) ((tokrow)->lp - (tokrow)->bp) char *basepath( char *fname ); extern char *outp; extern Token nltoken; extern Source *cursource; extern char *curtime; extern int incdepth; extern int ifdepth; extern int ifsatisfied[NIF]; extern int Mflag; extern int skipping; extern int verbose; extern int Cplusplus; extern Nlist *kwdefined; extern Includelist includelist[NINCLUDE]; extern char wd[]; #ifndef _WIN32 #include #else #include #endif #include openarena_0.8.8.orig/code/tools/lcc/cpp/unix.c0000644000175000017500000000505511656310262017755 0ustar smcvsmcv#include #include #include #include #include "cpp.h" extern int lcc_getopt(int, char *const *, const char *); extern char *optarg, rcsid[]; extern int optind; int verbose; int Mflag; /* only print active include files */ char *objname; /* "src.$O: " */ int Cplusplus = 1; void setup(int argc, char **argv) { int c, fd, i; char *fp, *dp; Tokenrow tr; extern void setup_kwtab(void); uchar *includeDirs[ NINCLUDE ] = { 0 }; int numIncludeDirs = 0; setup_kwtab(); while ((c = lcc_getopt(argc, argv, "MNOVv+I:D:U:F:lg")) != -1) switch (c) { case 'N': for (i=0; i", -1, optarg); maketokenrow(3, &tr); gettokens(&tr, 1); doadefine(&tr, c); unsetsource(); break; case 'M': Mflag++; break; case 'v': fprintf(stderr, "%s %s\n", argv[0], rcsid); break; case 'V': verbose++; break; case '+': Cplusplus++; break; default: break; } dp = "."; fp = ""; fd = 0; if (optind #include #include #include "cpp.h" /* * lexical FSM encoding * when in state state, and one of the characters * in ch arrives, enter nextstate. * States >= S_SELF are either final, or at least require special action. * In 'fsm' there is a line for each state X charset X nextstate. * List chars that overwrite previous entries later (e.g. C_ALPH * can be overridden by '_' by a later entry; and C_XX is the * the universal set, and should always be first. * States above S_SELF are represented in the big table as negative values. * S_SELF and S_SELFB encode the resulting token type in the upper bits. * These actions differ in that S_SELF doesn't have a lookahead char, * S_SELFB does. * * The encoding is blown out into a big table for time-efficiency. * Entries have * nextstate: 6 bits; ?\ marker: 1 bit; tokentype: 9 bits. */ #define MAXSTATE 32 #define ACT(tok,act) ((tok<<7)+act) #define QBSBIT 0100 #define GETACT(st) (st>>7)&0x1ff /* character classes */ #define C_WS 1 #define C_ALPH 2 #define C_NUM 3 #define C_EOF 4 #define C_XX 5 enum state { START=0, NUM1, NUM2, NUM3, ID1, ST1, ST2, ST3, COM1, COM2, COM3, COM4, CC1, CC2, WS1, PLUS1, MINUS1, STAR1, SLASH1, PCT1, SHARP1, CIRC1, GT1, GT2, LT1, LT2, OR1, AND1, ASG1, NOT1, DOTS1, S_SELF=MAXSTATE, S_SELFB, S_EOF, S_NL, S_EOFSTR, S_STNL, S_COMNL, S_EOFCOM, S_COMMENT, S_EOB, S_WS, S_NAME }; int tottok; int tokkind[256]; struct fsm { int state; /* if in this state */ uchar ch[4]; /* and see one of these characters */ int nextstate; /* enter this state if +ve */ }; /*const*/ struct fsm fsm[] = { /* start state */ {START, { C_XX }, ACT(UNCLASS,S_SELF)}, {START, { ' ', '\t', '\v' }, WS1}, {START, { C_NUM }, NUM1}, {START, { '.' }, NUM3}, {START, { C_ALPH }, ID1}, {START, { 'L' }, ST1}, {START, { '"' }, ST2}, {START, { '\'' }, CC1}, {START, { '/' }, COM1}, {START, { EOFC }, S_EOF}, {START, { '\n' }, S_NL}, {START, { '-' }, MINUS1}, {START, { '+' }, PLUS1}, {START, { '<' }, LT1}, {START, { '>' }, GT1}, {START, { '=' }, ASG1}, {START, { '!' }, NOT1}, {START, { '&' }, AND1}, {START, { '|' }, OR1}, {START, { '#' }, SHARP1}, {START, { '%' }, PCT1}, {START, { '[' }, ACT(SBRA,S_SELF)}, {START, { ']' }, ACT(SKET,S_SELF)}, {START, { '(' }, ACT(LP,S_SELF)}, {START, { ')' }, ACT(RP,S_SELF)}, {START, { '*' }, STAR1}, {START, { ',' }, ACT(COMMA,S_SELF)}, {START, { '?' }, ACT(QUEST,S_SELF)}, {START, { ':' }, ACT(COLON,S_SELF)}, {START, { ';' }, ACT(SEMIC,S_SELF)}, {START, { '{' }, ACT(CBRA,S_SELF)}, {START, { '}' }, ACT(CKET,S_SELF)}, {START, { '~' }, ACT(TILDE,S_SELF)}, {START, { '^' }, CIRC1}, /* saw a digit */ {NUM1, { C_XX }, ACT(NUMBER,S_SELFB)}, {NUM1, { C_NUM, C_ALPH, '.' }, NUM1}, {NUM1, { 'E', 'e' }, NUM2}, {NUM1, { '_' }, ACT(NUMBER,S_SELFB)}, /* saw possible start of exponent, digits-e */ {NUM2, { C_XX }, ACT(NUMBER,S_SELFB)}, {NUM2, { '+', '-' }, NUM1}, {NUM2, { C_NUM, C_ALPH }, NUM1}, {NUM2, { '_' }, ACT(NUMBER,S_SELFB)}, /* saw a '.', which could be a number or an operator */ {NUM3, { C_XX }, ACT(DOT,S_SELFB)}, {NUM3, { '.' }, DOTS1}, {NUM3, { C_NUM }, NUM1}, {DOTS1, { C_XX }, ACT(UNCLASS, S_SELFB)}, {DOTS1, { C_NUM }, NUM1}, {DOTS1, { '.' }, ACT(ELLIPS, S_SELF)}, /* saw a letter or _ */ {ID1, { C_XX }, ACT(NAME,S_NAME)}, {ID1, { C_ALPH, C_NUM }, ID1}, /* saw L (start of wide string?) */ {ST1, { C_XX }, ACT(NAME,S_NAME)}, {ST1, { C_ALPH, C_NUM }, ID1}, {ST1, { '"' }, ST2}, {ST1, { '\'' }, CC1}, /* saw " beginning string */ {ST2, { C_XX }, ST2}, {ST2, { '"' }, ACT(STRING, S_SELF)}, {ST2, { '\\' }, ST3}, {ST2, { '\n' }, S_STNL}, {ST2, { EOFC }, S_EOFSTR}, /* saw \ in string */ {ST3, { C_XX }, ST2}, {ST3, { '\n' }, S_STNL}, {ST3, { EOFC }, S_EOFSTR}, /* saw ' beginning character const */ {CC1, { C_XX }, CC1}, {CC1, { '\'' }, ACT(CCON, S_SELF)}, {CC1, { '\\' }, CC2}, {CC1, { '\n' }, S_STNL}, {CC1, { EOFC }, S_EOFSTR}, /* saw \ in ccon */ {CC2, { C_XX }, CC1}, {CC2, { '\n' }, S_STNL}, {CC2, { EOFC }, S_EOFSTR}, /* saw /, perhaps start of comment */ {COM1, { C_XX }, ACT(SLASH, S_SELFB)}, {COM1, { '=' }, ACT(ASSLASH, S_SELF)}, {COM1, { '*' }, COM2}, {COM1, { '/' }, COM4}, /* saw / then *, start of comment */ {COM2, { C_XX }, COM2}, {COM2, { '\n' }, S_COMNL}, {COM2, { '*' }, COM3}, {COM2, { EOFC }, S_EOFCOM}, /* saw the * possibly ending a comment */ {COM3, { C_XX }, COM2}, {COM3, { '\n' }, S_COMNL}, {COM3, { '*' }, COM3}, {COM3, { '/' }, S_COMMENT}, /* // comment */ {COM4, { C_XX }, COM4}, {COM4, { '\n' }, S_NL}, {COM4, { EOFC }, S_EOFCOM}, /* saw white space, eat it up */ {WS1, { C_XX }, S_WS}, {WS1, { ' ', '\t', '\v' }, WS1}, /* saw -, check --, -=, -> */ {MINUS1, { C_XX }, ACT(MINUS, S_SELFB)}, {MINUS1, { '-' }, ACT(MMINUS, S_SELF)}, {MINUS1, { '=' }, ACT(ASMINUS,S_SELF)}, {MINUS1, { '>' }, ACT(ARROW,S_SELF)}, /* saw +, check ++, += */ {PLUS1, { C_XX }, ACT(PLUS, S_SELFB)}, {PLUS1, { '+' }, ACT(PPLUS, S_SELF)}, {PLUS1, { '=' }, ACT(ASPLUS, S_SELF)}, /* saw <, check <<, <<=, <= */ {LT1, { C_XX }, ACT(LT, S_SELFB)}, {LT1, { '<' }, LT2}, {LT1, { '=' }, ACT(LEQ, S_SELF)}, {LT2, { C_XX }, ACT(LSH, S_SELFB)}, {LT2, { '=' }, ACT(ASLSH, S_SELF)}, /* saw >, check >>, >>=, >= */ {GT1, { C_XX }, ACT(GT, S_SELFB)}, {GT1, { '>' }, GT2}, {GT1, { '=' }, ACT(GEQ, S_SELF)}, {GT2, { C_XX }, ACT(RSH, S_SELFB)}, {GT2, { '=' }, ACT(ASRSH, S_SELF)}, /* = */ {ASG1, { C_XX }, ACT(ASGN, S_SELFB)}, {ASG1, { '=' }, ACT(EQ, S_SELF)}, /* ! */ {NOT1, { C_XX }, ACT(NOT, S_SELFB)}, {NOT1, { '=' }, ACT(NEQ, S_SELF)}, /* & */ {AND1, { C_XX }, ACT(AND, S_SELFB)}, {AND1, { '&' }, ACT(LAND, S_SELF)}, {AND1, { '=' }, ACT(ASAND, S_SELF)}, /* | */ {OR1, { C_XX }, ACT(OR, S_SELFB)}, {OR1, { '|' }, ACT(LOR, S_SELF)}, {OR1, { '=' }, ACT(ASOR, S_SELF)}, /* # */ {SHARP1, { C_XX }, ACT(SHARP, S_SELFB)}, {SHARP1, { '#' }, ACT(DSHARP, S_SELF)}, /* % */ {PCT1, { C_XX }, ACT(PCT, S_SELFB)}, {PCT1, { '=' }, ACT(ASPCT, S_SELF)}, /* * */ {STAR1, { C_XX }, ACT(STAR, S_SELFB)}, {STAR1, { '=' }, ACT(ASSTAR, S_SELF)}, /* ^ */ {CIRC1, { C_XX }, ACT(CIRC, S_SELFB)}, {CIRC1, { '=' }, ACT(ASCIRC, S_SELF)}, {-1} }; /* first index is char, second is state */ /* increase #states to power of 2 to encourage use of shift */ short bigfsm[256][MAXSTATE]; void expandlex(void) { /*const*/ struct fsm *fp; int i, j, nstate; for (fp = fsm; fp->state>=0; fp++) { for (i=0; fp->ch[i]; i++) { nstate = fp->nextstate; if (nstate >= S_SELF) nstate = ~nstate; switch (fp->ch[i]) { case C_XX: /* random characters */ for (j=0; j<256; j++) bigfsm[j][fp->state] = nstate; continue; case C_ALPH: for (j=0; j<=256; j++) if (('a'<=j&&j<='z') || ('A'<=j&&j<='Z') || j=='_') bigfsm[j][fp->state] = nstate; continue; case C_NUM: for (j='0'; j<='9'; j++) bigfsm[j][fp->state] = nstate; continue; default: bigfsm[fp->ch[i]][fp->state] = nstate; } } } /* install special cases for ? (trigraphs), \ (splicing), runes, and EOB */ for (i=0; i0) bigfsm[j][i] = ~bigfsm[j][i]; bigfsm[j][i] &= ~QBSBIT; } bigfsm[EOB][i] = ~S_EOB; if (bigfsm[EOFC][i]>=0) bigfsm[EOFC][i] = ~S_EOF; } } void fixlex(void) { /* do C++ comments? */ if (Cplusplus==0) bigfsm['/'][COM1] = bigfsm['x'][COM1]; } /* * fill in a row of tokens from input, terminated by NL or END * First token is put at trp->lp. * Reset is non-zero when the input buffer can be "rewound." * The value is a flag indicating that possible macros have * been seen in the row. */ int gettokens(Tokenrow *trp, int reset) { register int c, state, oldstate; register uchar *ip; register Token *tp, *maxp; int runelen; Source *s = cursource; int nmac = 0; tp = trp->lp; ip = s->inp; if (reset) { s->lineinc = 0; if (ip>=s->inl) { /* nothing in buffer */ s->inl = s->inb; fillbuf(s); ip = s->inp = s->inb; } else if (ip >= s->inb+(3*INS/4)) { memmove(s->inb, ip, 4+s->inl-ip); s->inl = s->inb+(s->inl-ip); ip = s->inp = s->inb; } } maxp = &trp->bp[trp->max]; runelen = 1; for (;;) { continue2: if (tp>=maxp) { trp->lp = tp; tp = growtokenrow(trp); maxp = &trp->bp[trp->max]; } tp->type = UNCLASS; tp->hideset = 0; tp->t = ip; tp->wslen = 0; tp->flag = 0; state = START; for (;;) { oldstate = state; c = *ip; if ((state = bigfsm[c][state]) >= 0) { ip += runelen; runelen = 1; continue; } state = ~state; reswitch: switch (state&0177) { case S_SELF: ip += runelen; runelen = 1; case S_SELFB: tp->type = GETACT(state); tp->len = ip - tp->t; tp++; goto continue2; case S_NAME: /* like S_SELFB but with nmac check */ tp->type = NAME; tp->len = ip - tp->t; nmac |= quicklook(tp->t[0], tp->len>1?tp->t[1]:0); tp++; goto continue2; case S_WS: tp->wslen = ip - tp->t; tp->t = ip; state = START; continue; default: if ((state&QBSBIT)==0) { ip += runelen; runelen = 1; continue; } state &= ~QBSBIT; s->inp = ip; if (c=='?') { /* check trigraph */ if (trigraph(s)) { state = oldstate; continue; } goto reswitch; } if (c=='\\') { /* line-folding */ if (foldline(s)) { s->lineinc++; state = oldstate; continue; } goto reswitch; } error(WARNING, "Lexical botch in cpp"); ip += runelen; runelen = 1; continue; case S_EOB: s->inp = ip; fillbuf(cursource); state = oldstate; continue; case S_EOF: tp->type = END; tp->len = 0; s->inp = ip; if (tp!=trp->bp && (tp-1)->type!=NL && cursource->fd!=-1) error(WARNING,"No newline at end of file"); trp->lp = tp+1; return nmac; case S_STNL: error(ERROR, "Unterminated string or char const"); case S_NL: tp->t = ip; tp->type = NL; tp->len = 1; tp->wslen = 0; s->lineinc++; s->inp = ip+1; trp->lp = tp+1; return nmac; case S_EOFSTR: error(FATAL, "EOF in string or char constant"); break; case S_COMNL: s->lineinc++; state = COM2; ip += runelen; runelen = 1; if (ip >= s->inb+(7*INS/8)) { /* very long comment */ memmove(tp->t, ip, 4+s->inl-ip); s->inl -= ip-tp->t; ip = tp->t+1; } continue; case S_EOFCOM: error(WARNING, "EOF inside comment"); --ip; case S_COMMENT: ++ip; tp->t = ip; tp->t[-1] = ' '; tp->wslen = 1; state = START; continue; } break; } ip += runelen; runelen = 1; tp->len = ip - tp->t; tp++; } } /* have seen ?; handle the trigraph it starts (if any) else 0 */ int trigraph(Source *s) { int c; while (s->inp+2 >= s->inl && fillbuf(s)!=EOF) ; if (s->inp[1]!='?') return 0; c = 0; switch(s->inp[2]) { case '=': c = '#'; break; case '(': c = '['; break; case '/': c = '\\'; break; case ')': c = ']'; break; case '\'': c = '^'; break; case '<': c = '{'; break; case '!': c = '|'; break; case '>': c = '}'; break; case '-': c = '~'; break; } if (c) { *s->inp = c; memmove(s->inp+1, s->inp+3, s->inl-s->inp+2); s->inl -= 2; } return c; } int foldline(Source *s) { while (s->inp+1 >= s->inl && fillbuf(s)!=EOF) ; if (s->inp[1] == '\n') { memmove(s->inp, s->inp+2, s->inl-s->inp+3); s->inl -= 2; return 1; } return 0; } int fillbuf(Source *s) { int n, nr; nr = INS/8; if ((char *)s->inl+nr > (char *)s->inb+INS) error(FATAL, "Input buffer overflow"); if (s->fd<0 || (n=read(s->fd, (char *)s->inl, INS/8)) <= 0) n = 0; if ((*s->inp&0xff) == EOB) /* sentinel character appears in input */ *s->inp = EOFC; s->inl += n; s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOB; if (n==0) { s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOFC; return EOF; } return 0; } /* * Push down to new source of characters. * If fd>0 and str==NULL, then from a file `name'; * if fd==-1 and str, then from the string. */ Source * setsource(char *name, int fd, char *str) { Source *s = new(Source); int len; s->line = 1; s->lineinc = 0; s->fd = fd; s->filename = name; s->next = cursource; s->ifdepth = 0; cursource = s; /* slop at right for EOB */ if (str) { len = strlen(str); s->inb = domalloc(len+4); s->inp = s->inb; strncpy((char *)s->inp, str, len); } else { s->inb = domalloc(INS+4); s->inp = s->inb; len = 0; } s->inl = s->inp+len; s->inl[0] = s->inl[1] = EOB; return s; } void unsetsource(void) { Source *s = cursource; if (s->fd>=0) { close(s->fd); dofree(s->inb); } cursource = s->next; dofree(s); } openarena_0.8.8.orig/code/tools/lcc/cpp/include.c0000644000175000017500000000626011656310262020414 0ustar smcvsmcv#include #include #include #include "cpp.h" Includelist includelist[NINCLUDE]; extern char *objname; void appendDirToIncludeList( char *dir ) { int i; char *fqdir; fqdir = (char *)newstring( (uchar *)includelist[NINCLUDE-1].file, 256, 0 ); strcat( fqdir, "/" ); strcat( fqdir, dir ); //avoid adding it more than once for (i=NINCLUDE-2; i>=0; i--) { if (includelist[i].file && !strcmp (includelist[i].file, fqdir)) { return; } } for (i=NINCLUDE-2; i>=0; i--) { if (includelist[i].file==NULL) { includelist[i].always = 1; includelist[i].file = fqdir; break; } } if (i<0) error(FATAL, "Too many -I directives"); } void doinclude(Tokenrow *trp) { char fname[256], iname[256]; Includelist *ip; int angled, len, fd, i; trp->tp += 1; if (trp->tp>=trp->lp) goto syntax; if (trp->tp->type!=STRING && trp->tp->type!=LT) { len = trp->tp - trp->bp; expandrow(trp, ""); trp->tp = trp->bp+len; } if (trp->tp->type==STRING) { len = trp->tp->len-2; if (len > sizeof(fname) - 1) len = sizeof(fname) - 1; strncpy(fname, (char*)trp->tp->t+1, len); angled = 0; } else if (trp->tp->type==LT) { len = 0; trp->tp++; while (trp->tp->type!=GT) { if (trp->tp>trp->lp || len+trp->tp->len+2 >= sizeof(fname)) goto syntax; strncpy(fname+len, (char*)trp->tp->t, trp->tp->len); len += trp->tp->len; trp->tp++; } angled = 1; } else goto syntax; trp->tp += 2; if (trp->tp < trp->lp || len==0) goto syntax; fname[len] = '\0'; appendDirToIncludeList( basepath( fname ) ); if (fname[0]=='/') { fd = open(fname, 0); strcpy(iname, fname); } else for (fd = -1,i=NINCLUDE-1; i>=0; i--) { ip = &includelist[i]; if (ip->file==NULL || ip->deleted || (angled && ip->always==0)) continue; if (strlen(fname)+strlen(ip->file)+2 > sizeof(iname)) continue; strcpy(iname, ip->file); strcat(iname, "/"); strcat(iname, fname); if ((fd = open(iname, 0)) >= 0) break; } if ( Mflag>1 || (!angled&&Mflag==1) ) { write(1,objname,strlen(objname)); write(1,iname,strlen(iname)); write(1,"\n",1); } if (fd >= 0) { if (++incdepth > 10) error(FATAL, "#include too deeply nested"); setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd, NULL); genline(); } else { trp->tp = trp->bp+2; error(ERROR, "Could not find include file %r", trp); } return; syntax: error(ERROR, "Syntax error in #include"); return; } /* * Generate a line directive for cursource */ void genline(void) { static Token ta = { UNCLASS }; static Tokenrow tr = { &ta, &ta, &ta+1, 1 }; uchar *p; ta.t = p = (uchar*)outp; strcpy((char*)p, "#line "); p += sizeof("#line ")-1; p = (uchar*)outnum((char*)p, cursource->line); *p++ = ' '; *p++ = '"'; if (cursource->filename[0]!='/' && wd[0]) { strcpy((char*)p, wd); p += strlen(wd); *p++ = '/'; } strcpy((char*)p, cursource->filename); p += strlen((char*)p); *p++ = '"'; *p++ = '\n'; ta.len = (char*)p-outp; outp = (char*)p; tr.tp = tr.bp; puttokens(&tr); } void setobjname(char *f) { int n = strlen(f); objname = (char*)domalloc(n+5); strcpy(objname,f); if(objname[n-2]=='.'){ strcpy(objname+n-1,"$O: "); }else{ strcpy(objname+n,"$O: "); } } openarena_0.8.8.orig/code/tools/lcc/src/0000755000175000017500000000000011720470210016616 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/src/config.h0000644000175000017500000000464511656310262020255 0ustar smcvsmcvtypedef struct { unsigned char max_unaligned_load; Symbol (*rmap)(int); void (*blkfetch)(int size, int off, int reg, int tmp); void (*blkstore)(int size, int off, int reg, int tmp); void (*blkloop)(int dreg, int doff, int sreg, int soff, int size, int tmps[]); void (*_label)(Node); int (*_rule)(void*, int); short **_nts; void (*_kids)(Node, int, Node*); char **_string; char **_templates; char *_isinstruction; char **_ntname; void (*emit2)(Node); void (*doarg)(Node); void (*target)(Node); void (*clobber)(Node); } Xinterface; extern int askregvar(Symbol, Symbol); extern void blkcopy(int, int, int, int, int, int[]); extern int getregnum(Node); extern int mayrecalc(Node); extern int mkactual(int, int); extern void mkauto(Symbol); extern Symbol mkreg(char *, int, int, int); extern Symbol mkwildcard(Symbol *); extern int move(Node); extern int notarget(Node); extern void parseflags(int, char **); extern int range(Node, int, int); extern unsigned regloc(Symbol); /* omit */ extern void rtarget(Node, int, Symbol); extern void setreg(Node, Symbol); extern void spill(unsigned, int, Node); extern int widens(Node); extern int argoffset, maxargoffset; extern int bflag, dflag; extern int dalign, salign; extern int framesize; extern unsigned freemask[], usedmask[]; extern int offset, maxoffset; extern int swap; extern unsigned tmask[], vmask[]; typedef struct { unsigned listed:1; unsigned registered:1; unsigned emitted:1; unsigned copy:1; unsigned equatable:1; unsigned spills:1; unsigned mayrecalc:1; void *state; short inst; Node kids[3]; Node prev, next; Node prevuse; short argno; } Xnode; typedef struct { Symbol vbl; short set; short number; unsigned mask; } *Regnode; enum { IREG=0, FREG=1 }; typedef struct { char *name; unsigned int eaddr; /* omit */ int offset; Node lastuse; int usecount; Regnode regnode; Symbol *wildcard; } Xsymbol; enum { RX=2 }; typedef struct { int offset; unsigned freemask[2]; } Env; #define LBURG_MAX SHRT_MAX enum { VREG=(44<<4) }; /* Exported for the front end */ extern void blockbeg(Env *); extern void blockend(Env *); extern void emit(Node); extern Node gen(Node); extern unsigned emitbin(Node, int); #ifdef NDEBUG #define debug(x) (void)0 #else #define debug(x) (void)(dflag&&((x),0)) #endif openarena_0.8.8.orig/code/tools/lcc/src/dag.c0000644000175000017500000005317311656310262017536 0ustar smcvsmcv#include "c.h" #define iscall(op) (generic(op) == CALL \ || (IR->mulops_calls \ && (generic(op)==DIV||generic(op)==MOD||generic(op)==MUL) \ && ( optype(op)==U || optype(op)==I))) static Node forest; static struct dag { struct node node; struct dag *hlink; } *buckets[16]; int nodecount; static Tree firstarg; int assignargs = 1; int prunetemps = -1; static Node *tail; static int depth = 0; static Node replace(Node); static Node prune(Node); static Node asgnnode(Symbol, Node); static struct dag *dagnode(int, Node, Node, Symbol); static Symbol equated(Symbol); static void fixup(Node); static void labelnode(int); static void list(Node); static void kill(Symbol); static Node node(int, Node, Node, Symbol); static void printdag1(Node, int, int); static void printnode(Node, int, int); static void reset(void); static Node tmpnode(Node); static void typestab(Symbol, void *); static Node undag(Node); static Node visit(Node, int); static void unlist(void); void walk(Tree tp, int tlab, int flab) { listnodes(tp, tlab, flab); if (forest) { Node list = forest->link; forest->link = NULL; if (!IR->wants_dag) list = undag(list); code(Gen)->u.forest = list; forest = NULL; } reset(); deallocate(STMT); } static Node node(int op, Node l, Node r, Symbol sym) { int i; struct dag *p; i = (opindex(op)^((unsigned long)sym>>2))&(NELEMS(buckets)-1); for (p = buckets[i]; p; p = p->hlink) if (p->node.op == op && p->node.syms[0] == sym && p->node.kids[0] == l && p->node.kids[1] == r) return &p->node; p = dagnode(op, l, r, sym); p->hlink = buckets[i]; buckets[i] = p; ++nodecount; return &p->node; } static struct dag *dagnode(int op, Node l, Node r, Symbol sym) { struct dag *p; NEW0(p, FUNC); p->node.op = op; if ((p->node.kids[0] = l) != NULL) ++l->count; if ((p->node.kids[1] = r) != NULL) ++r->count; p->node.syms[0] = sym; return p; } Node newnode(int op, Node l, Node r, Symbol sym) { return &dagnode(op, l, r, sym)->node; } static void kill(Symbol p) { int i; struct dag **q; for (i = 0; i < NELEMS(buckets); i++) for (q = &buckets[i]; *q; ) if (generic((*q)->node.op) == INDIR && (!isaddrop((*q)->node.kids[0]->op) || (*q)->node.kids[0]->syms[0] == p)) { *q = (*q)->hlink; --nodecount; } else q = &(*q)->hlink; } static void reset(void) { if (nodecount > 0) memset(buckets, 0, sizeof buckets); nodecount = 0; } Node listnodes(Tree tp, int tlab, int flab) { Node p = NULL, l, r; int op; assert(tlab || flab || (tlab == 0 && flab == 0)); if (tp == NULL) return NULL; if (tp->node) return tp->node; op = tp->op + sizeop(tp->type->size); switch (generic(tp->op)) { case AND: { if (depth++ == 0) reset(); if (flab) { listnodes(tp->kids[0], 0, flab); listnodes(tp->kids[1], 0, flab); } else { listnodes(tp->kids[0], 0, flab = genlabel(1)); listnodes(tp->kids[1], tlab, 0); labelnode(flab); } depth--; } break; case OR: { if (depth++ == 0) reset(); if (tlab) { listnodes(tp->kids[0], tlab, 0); listnodes(tp->kids[1], tlab, 0); } else { tlab = genlabel(1); listnodes(tp->kids[0], tlab, 0); listnodes(tp->kids[1], 0, flab); labelnode(tlab); } depth--; } break; case NOT: { return listnodes(tp->kids[0], flab, tlab); } case COND: { Tree q = tp->kids[1]; assert(tlab == 0 && flab == 0); if (tp->u.sym) addlocal(tp->u.sym); flab = genlabel(2); listnodes(tp->kids[0], 0, flab); assert(q && q->op == RIGHT); reset(); listnodes(q->kids[0], 0, 0); if (forest->op == LABEL+V) { equatelab(forest->syms[0], findlabel(flab + 1)); unlist(); } list(jump(flab + 1)); labelnode(flab); listnodes(q->kids[1], 0, 0); if (forest->op == LABEL+V) { equatelab(forest->syms[0], findlabel(flab + 1)); unlist(); } labelnode(flab + 1); if (tp->u.sym) p = listnodes(idtree(tp->u.sym), 0, 0); } break; case CNST: { Type ty = unqual(tp->type); assert(ty->u.sym); if (tlab || flab) { assert(ty == inttype); if (tlab && tp->u.v.i != 0) list(jump(tlab)); else if (flab && tp->u.v.i == 0) list(jump(flab)); } else if (ty->u.sym->addressed) p = listnodes(cvtconst(tp), 0, 0); else p = node(op, NULL, NULL, constant(ty, tp->u.v)); } break; case RIGHT: { if ( tp->kids[0] && tp->kids[1] && generic(tp->kids[1]->op) == ASGN && ((generic(tp->kids[0]->op) == INDIR && tp->kids[0]->kids[0] == tp->kids[1]->kids[0]) || (tp->kids[0]->op == FIELD && tp->kids[0] == tp->kids[1]->kids[0]))) { assert(tlab == 0 && flab == 0); if (generic(tp->kids[0]->op) == INDIR) { p = listnodes(tp->kids[0], 0, 0); list(p); listnodes(tp->kids[1], 0, 0); } else { assert(generic(tp->kids[0]->kids[0]->op) == INDIR); list(listnodes(tp->kids[0]->kids[0], 0, 0)); p = listnodes(tp->kids[0], 0, 0); listnodes(tp->kids[1], 0, 0); } } else if (tp->kids[1]) { listnodes(tp->kids[0], 0, 0); p = listnodes(tp->kids[1], tlab, flab); } else p = listnodes(tp->kids[0], tlab, flab); } break; case JUMP: { assert(tlab == 0 && flab == 0); assert(tp->u.sym == 0); assert(tp->kids[0]); l = listnodes(tp->kids[0], 0, 0); list(newnode(JUMP+V, l, NULL, NULL)); reset(); } break; case CALL: { Tree save = firstarg; firstarg = NULL; assert(tlab == 0 && flab == 0); if (tp->op == CALL+B && !IR->wants_callb) { Tree arg0 = tree(ARG+P, tp->kids[1]->type, tp->kids[1], NULL); if (IR->left_to_right) firstarg = arg0; l = listnodes(tp->kids[0], 0, 0); if (!IR->left_to_right || firstarg) { firstarg = NULL; listnodes(arg0, 0, 0); } p = newnode(CALL+V, l, NULL, NULL); } else { l = listnodes(tp->kids[0], 0, 0); r = listnodes(tp->kids[1], 0, 0); p = newnode(tp->op == CALL+B ? tp->op : op, l, r, NULL); } NEW0(p->syms[0], FUNC); assert(isptr(tp->kids[0]->type)); assert(isfunc(tp->kids[0]->type->type)); p->syms[0]->type = tp->kids[0]->type->type; list(p); reset(); cfunc->u.f.ncalls++; firstarg = save; } break; case ARG: { assert(tlab == 0 && flab == 0); if (IR->left_to_right) listnodes(tp->kids[1], 0, 0); if (firstarg) { Tree arg = firstarg; firstarg = NULL; listnodes(arg, 0, 0); } l = listnodes(tp->kids[0], 0, 0); list(newnode(tp->op == ARG+B ? tp->op : op, l, NULL, NULL)); forest->syms[0] = intconst(tp->type->size); forest->syms[1] = intconst(tp->type->align); if (!IR->left_to_right) listnodes(tp->kids[1], 0, 0); } break; case EQ: case NE: case GT: case GE: case LE: case LT: { assert(tp->u.sym == 0); assert(errcnt || tlab || flab); l = listnodes(tp->kids[0], 0, 0); r = listnodes(tp->kids[1], 0, 0); assert(errcnt || opkind(l->op) == opkind(r->op)); assert(errcnt || optype(op) == optype(l->op)); if (tlab) assert(flab == 0), list(newnode(generic(tp->op) + opkind(l->op), l, r, findlabel(tlab))); else if (flab) { switch (generic(tp->op)) { case EQ: op = NE; break; case NE: op = EQ; break; case GT: op = LE; break; case LT: op = GE; break; case GE: op = LT; break; case LE: op = GT; break; default: assert(0); } list(newnode(op + opkind(l->op), l, r, findlabel(flab))); } if (forest && forest->syms[0]) forest->syms[0]->ref++; } break; case ASGN: { assert(tlab == 0 && flab == 0); if (tp->kids[0]->op == FIELD) { Tree x = tp->kids[0]->kids[0]; Field f = tp->kids[0]->u.field; assert(generic(x->op) == INDIR); reset(); l = listnodes(lvalue(x), 0, 0); if (fieldsize(f) < 8*f->type->size) { unsigned int fmask = fieldmask(f); unsigned int mask = fmask<kids[1]; if ((q->op == CNST+I && q->u.v.i == 0) || (q->op == CNST+U && q->u.v.u == 0)) q = bittree(BAND, x, cnsttree(unsignedtype, (unsigned long)~mask)); else if ((q->op == CNST+I && (q->u.v.i&fmask) == fmask) || (q->op == CNST+U && (q->u.v.u&fmask) == fmask)) q = bittree(BOR, x, cnsttree(unsignedtype, (unsigned long)mask)); else { listnodes(q, 0, 0); q = bittree(BOR, bittree(BAND, rvalue(lvalue(x)), cnsttree(unsignedtype, (unsigned long)~mask)), bittree(BAND, shtree(LSH, cast(q, unsignedtype), cnsttree(unsignedtype, (unsigned long)fieldright(f))), cnsttree(unsignedtype, (unsigned long)mask))); } r = listnodes(q, 0, 0); op = ASGN + ttob(q->type); } else { r = listnodes(tp->kids[1], 0, 0); op = ASGN + ttob(tp->kids[1]->type); } } else { l = listnodes(tp->kids[0], 0, 0); r = listnodes(tp->kids[1], 0, 0); } list(newnode(tp->op == ASGN+B ? tp->op : op, l, r, NULL)); forest->syms[0] = intconst(tp->kids[1]->type->size); forest->syms[1] = intconst(tp->kids[1]->type->align); if (isaddrop(tp->kids[0]->op) && !tp->kids[0]->u.sym->computed) kill(tp->kids[0]->u.sym); else reset(); p = listnodes(tp->kids[1], 0, 0); } break; case BOR: case BAND: case BXOR: case ADD: case SUB: case RSH: case LSH: { assert(tlab == 0 && flab == 0); l = listnodes(tp->kids[0], 0, 0); r = listnodes(tp->kids[1], 0, 0); p = node(op, l, r, NULL); } break; case DIV: case MUL: case MOD: { assert(tlab == 0 && flab == 0); l = listnodes(tp->kids[0], 0, 0); r = listnodes(tp->kids[1], 0, 0); p = node(op, l, r, NULL); if (IR->mulops_calls && isint(tp->type)) { list(p); cfunc->u.f.ncalls++; } } break; case RET: { assert(tlab == 0 && flab == 0); l = listnodes(tp->kids[0], 0, 0); list(newnode(op, l, NULL, NULL)); } break; case CVF: case CVI: case CVP: case CVU: { assert(tlab == 0 && flab == 0); assert(optype(tp->kids[0]->op) != optype(tp->op) || tp->kids[0]->type->size != tp->type->size); l = listnodes(tp->kids[0], 0, 0); p = node(op, l, NULL, intconst(tp->kids[0]->type->size)); } break; case BCOM: case NEG: { assert(tlab == 0 && flab == 0); l = listnodes(tp->kids[0], 0, 0); p = node(op, l, NULL, NULL); } break; case INDIR: { Type ty = tp->kids[0]->type; assert(tlab == 0 && flab == 0); l = listnodes(tp->kids[0], 0, 0); if (isptr(ty)) ty = unqual(ty)->type; if (isvolatile(ty) || (isstruct(ty) && unqual(ty)->u.sym->u.s.vfields)) p = newnode(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL); else p = node(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL); } break; case FIELD: { Tree q = tp->kids[0]; if (tp->type == inttype) { long n = fieldleft(tp->u.field); q = shtree(RSH, shtree(LSH, q, cnsttree(inttype, n)), cnsttree(inttype, n + fieldright(tp->u.field))); } else if (fieldsize(tp->u.field) < 8*tp->u.field->type->size) q = bittree(BAND, shtree(RSH, q, cnsttree(inttype, (long)fieldright(tp->u.field))), cnsttree(unsignedtype, (unsigned long)fieldmask(tp->u.field))); assert(tlab == 0 && flab == 0); p = listnodes(q, 0, 0); } break; case ADDRG: case ADDRF: { assert(tlab == 0 && flab == 0); p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym); } break; case ADDRL: { assert(tlab == 0 && flab == 0); if (tp->u.sym->temporary) addlocal(tp->u.sym); p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym); } break; default:assert(0); } tp->node = p; return p; } static void list(Node p) { if (p && p->link == NULL) { if (forest) { p->link = forest->link; forest->link = p; } else p->link = p; forest = p; } } static void labelnode(int lab) { assert(lab); if (forest && forest->op == LABEL+V) equatelab(findlabel(lab), forest->syms[0]); else list(newnode(LABEL+V, NULL, NULL, findlabel(lab))); reset(); } static void unlist(void) { Node p; assert(forest); assert(forest != forest->link); p = forest->link; while (p->link != forest) p = p->link; p->link = forest->link; forest = p; } Tree cvtconst(Tree p) { Symbol q = constant(p->type, p->u.v); Tree e; if (q->u.c.loc == NULL) q->u.c.loc = genident(STATIC, p->type, GLOBAL); if (isarray(p->type)) { e = simplify(ADDRG, atop(p->type), NULL, NULL); e->u.sym = q->u.c.loc; } else e = idtree(q->u.c.loc); return e; } void gencode(Symbol caller[], Symbol callee[]) { Code cp; Coordinate save; if (prunetemps == -1) prunetemps = !IR->wants_dag; save = src; if (assignargs) { int i; Symbol p, q; cp = codehead.next->next; codelist = codehead.next; for (i = 0; (p = callee[i]) != NULL && (q = caller[i]) != NULL; i++) if (p->sclass != q->sclass || p->type != q->type) walk(asgn(p, idtree(q)), 0, 0); codelist->next = cp; cp->prev = codelist; } if (glevel && IR->stabsym) { int i; Symbol p, q; for (i = 0; (p = callee[i]) != NULL && (q = caller[i]) != NULL; i++) { (*IR->stabsym)(p); if (p->sclass != q->sclass || p->type != q->type) (*IR->stabsym)(q); } swtoseg(CODE); } cp = codehead.next; for ( ; errcnt <= 0 && cp; cp = cp->next) switch (cp->kind) { case Address: (*IR->address)(cp->u.addr.sym, cp->u.addr.base, cp->u.addr.offset); break; case Blockbeg: { Symbol *p = cp->u.block.locals; (*IR->blockbeg)(&cp->u.block.x); for ( ; *p; p++) if ((*p)->ref != 0.0) (*IR->local)(*p); else if (glevel) (*IR->local)(*p); } break; case Blockend: (*IR->blockend)(&cp->u.begin->u.block.x); break; case Defpoint: src = cp->u.point.src; break; case Gen: case Jump: case Label: if (prunetemps) cp->u.forest = prune(cp->u.forest); fixup(cp->u.forest); cp->u.forest = (*IR->gen)(cp->u.forest); break; case Local: (*IR->local)(cp->u.var); break; case Switch: break; default: assert(0); } src = save; } static void fixup(Node p) { for ( ; p; p = p->link) switch (generic(p->op)) { case JUMP: if (specific(p->kids[0]->op) == ADDRG+P) p->kids[0]->syms[0] = equated(p->kids[0]->syms[0]); break; case LABEL: assert(p->syms[0] == equated(p->syms[0])); break; case EQ: case GE: case GT: case LE: case LT: case NE: assert(p->syms[0]); p->syms[0] = equated(p->syms[0]); } } static Symbol equated(Symbol p) { { Symbol q; for (q = p->u.l.equatedto; q; q = q->u.l.equatedto) assert(p != q); } while (p->u.l.equatedto) p = p->u.l.equatedto; return p; } void emitcode(void) { Code cp; Coordinate save; save = src; cp = codehead.next; for ( ; errcnt <= 0 && cp; cp = cp->next) switch (cp->kind) { case Address: break; case Blockbeg: if (glevel && IR->stabblock) { (*IR->stabblock)('{', cp->u.block.level - LOCAL, cp->u.block.locals); swtoseg(CODE); } break; case Blockend: if (glevel && IR->stabblock) { Code bp = cp->u.begin; foreach(bp->u.block.identifiers, bp->u.block.level, typestab, NULL); foreach(bp->u.block.types, bp->u.block.level, typestab, NULL); (*IR->stabblock)('}', bp->u.block.level - LOCAL, bp->u.block.locals); swtoseg(CODE); } break; case Defpoint: src = cp->u.point.src; if (glevel > 0 && IR->stabline) { (*IR->stabline)(&cp->u.point.src); swtoseg(CODE); } break; case Gen: case Jump: case Label: if (cp->u.forest) (*IR->emit)(cp->u.forest); break; case Local: if (glevel && IR->stabsym) { (*IR->stabsym)(cp->u.var); swtoseg(CODE); } break; case Switch: { int i; defglobal(cp->u.swtch.table, LIT); (*IR->defaddress)(equated(cp->u.swtch.labels[0])); for (i = 1; i < cp->u.swtch.size; i++) { long k = cp->u.swtch.values[i-1]; while (++k < cp->u.swtch.values[i]) assert(k < LONG_MAX), (*IR->defaddress)(equated(cp->u.swtch.deflab)); (*IR->defaddress)(equated(cp->u.swtch.labels[i])); } swtoseg(CODE); } break; default: assert(0); } src = save; } static Node undag(Node forest) { Node p; tail = &forest; for (p = forest; p; p = p->link) if (generic(p->op) == INDIR) { assert(p->count >= 1); visit(p, 1); if (p->syms[2]) { assert(p->syms[2]->u.t.cse); p->syms[2]->u.t.cse = NULL; addlocal(p->syms[2]); } } else if (iscall(p->op) && p->count >= 1) visit(p, 1); else { assert(p->count == 0), visit(p, 1); *tail = p; tail = &p->link; } *tail = NULL; return forest; } static Node replace(Node p) { if (p && ( generic(p->op) == INDIR && generic(p->kids[0]->op) == ADDRL && p->kids[0]->syms[0]->temporary && p->kids[0]->syms[0]->u.t.replace)) { p = p->kids[0]->syms[0]->u.t.cse; if (generic(p->op) == INDIR && isaddrop(p->kids[0]->op)) p = newnode(p->op, newnode(p->kids[0]->op, NULL, NULL, p->kids[0]->syms[0]), NULL, NULL); else if (generic(p->op) == ADDRG) p = newnode(p->op, NULL, NULL, p->syms[0]); else assert(0); p->count = 1; } else if (p) { p->kids[0] = replace(p->kids[0]); p->kids[1] = replace(p->kids[1]); } return p; } static Node prune(Node forest) { Node p, *tail = &forest; int count = 0; for (p = forest; p; p = p->link) { if (count > 0) { p->kids[0] = replace(p->kids[0]); p->kids[1] = replace(p->kids[1]); } if (( generic(p->op) == ASGN && generic(p->kids[0]->op) == ADDRL && p->kids[0]->syms[0]->temporary && p->kids[0]->syms[0]->u.t.cse == p->kids[1])) { Symbol tmp = p->kids[0]->syms[0]; if (!tmp->defined) (*IR->local)(tmp); tmp->defined = 1; if (( generic(p->kids[1]->op) == INDIR && isaddrop(p->kids[1]->kids[0]->op) && p->kids[1]->kids[0]->syms[0]->sclass == REGISTER) || (( generic(p->kids[1]->op) == INDIR && isaddrop(p->kids[1]->kids[0]->op)) && tmp->sclass == AUTO) || (generic(p->kids[1]->op) == ADDRG && tmp->sclass == AUTO)) { tmp->u.t.replace = 1; count++; continue; /* and omit the assignment */ } } /* keep the assignment and other roots */ *tail = p; tail = &(*tail)->link; } assert(*tail == NULL); return forest; } static Node visit(Node p, int listed) { if (p) { if (p->syms[2]) p = tmpnode(p); else if ((p->count <= 1 && !iscall(p->op)) || (p->count == 0 && iscall(p->op))) { p->kids[0] = visit(p->kids[0], 0); p->kids[1] = visit(p->kids[1], 0); } else if (specific(p->op) == ADDRL+P || specific(p->op) == ADDRF+P) { assert(!listed); p = newnode(p->op, NULL, NULL, p->syms[0]); p->count = 1; } else if (p->op == INDIR+B) { p = newnode(p->op, p->kids[0], NULL, NULL); p->count = 1; p->kids[0] = visit(p->kids[0], 0); p->kids[1] = visit(p->kids[1], 0); } else { p->kids[0] = visit(p->kids[0], 0); p->kids[1] = visit(p->kids[1], 0); p->syms[2] = temporary(REGISTER, btot(p->op, opsize(p->op))); assert(!p->syms[2]->defined); p->syms[2]->ref = 1; p->syms[2]->u.t.cse = p; *tail = asgnnode(p->syms[2], p); tail = &(*tail)->link; if (!listed) p = tmpnode(p); }; } return p; } static Node tmpnode(Node p) { Symbol tmp = p->syms[2]; assert(tmp); if (--p->count == 0) p->syms[2] = NULL; p = newnode(INDIR + ttob(tmp->type), newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), NULL, NULL); p->count = 1; return p; } static Node asgnnode(Symbol tmp, Node p) { p = newnode(ASGN + ttob(tmp->type), newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), p, NULL); p->syms[0] = intconst(tmp->type->size); p->syms[1] = intconst(tmp->type->align); return p; } /* printdag - print dag p on fd, or the node list if p == 0 */ void printdag(Node p, int fd) { FILE *f = fd == 1 ? stdout : stderr; printed(0); if (p == 0) { if ((p = forest) != NULL) do { p = p->link; printdag1(p, fd, 0); } while (p != forest); } else if (*printed(nodeid((Tree)p))) fprint(f, "node'%d printed above\n", nodeid((Tree)p)); else printdag1(p, fd, 0); } /* printdag1 - recursively print dag p */ static void printdag1(Node p, int fd, int lev) { int id, i; if (p == 0 || *printed(id = nodeid((Tree)p))) return; *printed(id) = 1; for (i = 0; i < NELEMS(p->kids); i++) printdag1(p->kids[i], fd, lev + 1); printnode(p, fd, lev); } /* printnode - print fields of dag p */ static void printnode(Node p, int fd, int lev) { if (p) { FILE *f = fd == 1 ? stdout : stderr; int i, id = nodeid((Tree)p); fprint(f, "%c%d%s", lev == 0 ? '\'' : '#', id, &" "[id < 10 ? 0 : id < 100 ? 1 : 2]); fprint(f, "%s count=%d", opname(p->op), p->count); for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++) fprint(f, " #%d", nodeid((Tree)p->kids[i])); if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type) fprint(f, " {%t}", p->syms[0]->type); else for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++) if (p->syms[i]->name) fprint(f, " %s", p->syms[i]->name); else fprint(f, " %p", p->syms[i]); fprint(f, "\n"); } } /* typestab - emit stab entries for p */ static void typestab(Symbol p, void *cl) { if (!isfunc(p->type) && (p->sclass == EXTERN || p->sclass == STATIC) && IR->stabsym) (*IR->stabsym)(p); else if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype) (*IR->stabtype)(p); } openarena_0.8.8.orig/code/tools/lcc/src/tree.c0000644000175000017500000001201511656310263017731 0ustar smcvsmcv#include "c.h" int where = STMT; static int warn; static int nid = 1; /* identifies trees & nodes in debugging output */ static struct nodeid { int printed; Tree node; } ids[500]; /* if ids[i].node == p, then p's id is i */ static void printtree1(Tree, int, int); Tree tree(int op, Type type, Tree left, Tree right) { Tree p; NEW0(p, where); p->op = op; p->type = type; p->kids[0] = left; p->kids[1] = right; return p; } Tree texpr(Tree (*f)(int), int tok, int a) { int save = where; Tree p; where = a; p = (*f)(tok); where = save; return p; } static Tree root1(Tree p) { if (p == NULL) return p; if (p->type == voidtype) warn++; switch (generic(p->op)) { case COND: { Tree q = p->kids[1]; assert(q && q->op == RIGHT); if (p->u.sym && q->kids[0] && generic(q->kids[0]->op) == ASGN) q->kids[0] = root1(q->kids[0]->kids[1]); else q->kids[0] = root1(q->kids[0]); if (p->u.sym && q->kids[1] && generic(q->kids[1]->op) == ASGN) q->kids[1] = root1(q->kids[1]->kids[1]); else q->kids[1] = root1(q->kids[1]); p->u.sym = 0; if (q->kids[0] == 0 && q->kids[1] == 0) p = root1(p->kids[0]); } break; case AND: case OR: if ((p->kids[1] = root1(p->kids[1])) == 0) p = root1(p->kids[0]); break; case NOT: if (warn++ == 0) warning("expression with no effect elided\n"); return root1(p->kids[0]); case RIGHT: if (p->kids[1] == 0) return root1(p->kids[0]); if (p->kids[0] && p->kids[0]->op == CALL+B && p->kids[1] && p->kids[1]->op == INDIR+B) /* avoid premature release of the CALL+B temporary */ return p->kids[0]; if (p->kids[0] && p->kids[0]->op == RIGHT && p->kids[1] == p->kids[0]->kids[0]) /* de-construct e++ construction */ return p->kids[0]->kids[1]; p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1])); return p->kids[0] || p->kids[1] ? p : (Tree)0; case EQ: case NE: case GT: case GE: case LE: case LT: case ADD: case SUB: case MUL: case DIV: case MOD: case LSH: case RSH: case BAND: case BOR: case BXOR: if (warn++ == 0) warning("expression with no effect elided\n"); p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1])); return p->kids[0] || p->kids[1] ? p : (Tree)0; case INDIR: if (p->type->size == 0 && unqual(p->type) != voidtype) warning("reference to `%t' elided\n", p->type); if (isptr(p->kids[0]->type) && isvolatile(p->kids[0]->type->type)) warning("reference to `volatile %t' elided\n", p->type); /* fall thru */ case CVI: case CVF: case CVU: case CVP: case NEG: case BCOM: case FIELD: if (warn++ == 0) warning("expression with no effect elided\n"); return root1(p->kids[0]); case ADDRL: case ADDRG: case ADDRF: case CNST: if (needconst) return p; if (warn++ == 0) warning("expression with no effect elided\n"); return NULL; case ARG: case ASGN: case CALL: case JUMP: case LABEL: break; default: assert(0); } return p; } Tree root(Tree p) { warn = 0; return root1(p); } char *opname(int op) { static char *opnames[] = { "", "CNST", "ARG", "ASGN", "INDIR", "CVC", "CVD", "CVF", "CVI", "CVP", "CVS", "CVU", "NEG", "CALL", "*LOAD*", "RET", "ADDRG", "ADDRF", "ADDRL", "ADD", "SUB", "LSH", "MOD", "RSH", "BAND", "BCOM", "BOR", "BXOR", "DIV", "MUL", "EQ", "GE", "GT", "LE", "LT", "NE", "JUMP", "LABEL", "AND", "NOT", "OR", "COND", "RIGHT", "FIELD" }, *suffixes[] = { "0", "F", "D", "C", "S", "I", "U", "P", "V", "B", "10","11","12","13","14","15" }; if (generic(op) >= AND && generic(op) <= FIELD && opsize(op) == 0) return opnames[opindex(op)]; return stringf("%s%s%s", opindex(op) > 0 && opindex(op) < NELEMS(opnames) ? opnames[opindex(op)] : stringd(opindex(op)), suffixes[optype(op)], opsize(op) > 0 ? stringd(opsize(op)) : ""); } int nodeid(Tree p) { int i = 1; ids[nid].node = p; while (ids[i].node != p) i++; if (i == nid) ids[nid++].printed = 0; return i; } /* printed - return pointer to ids[id].printed */ int *printed(int id) { if (id) return &ids[id].printed; nid = 1; return 0; } /* printtree - print tree p on fd */ void printtree(Tree p, int fd) { (void)printed(0); printtree1(p, fd, 1); } /* printtree1 - recursively print tree p */ static void printtree1(Tree p, int fd, int lev) { FILE *f = fd == 1 ? stdout : stderr; int i; static char blanks[] = " "; if (p == 0 || *printed(i = nodeid(p))) return; fprint(f, "#%d%S%S", i, blanks, i < 10 ? 2 : i < 100 ? 1 : 0, blanks, lev); fprint(f, "%s %t", opname(p->op), p->type); *printed(i) = 1; for (i = 0; i < NELEMS(p->kids); i++) if (p->kids[i]) fprint(f, " #%d", nodeid(p->kids[i])); if (p->op == FIELD && p->u.field) fprint(f, " %s %d..%d", p->u.field->name, fieldsize(p->u.field) + fieldright(p->u.field), fieldright(p->u.field)); else if (generic(p->op) == CNST) fprint(f, " %s", vtoa(p->type, p->u.v)); else if (p->u.sym) fprint(f, " %s", p->u.sym->name); if (p->node) fprint(f, " node=%p", p->node); fprint(f, "\n"); for (i = 0; i < NELEMS(p->kids); i++) printtree1(p->kids[i], fd, lev + 1); } openarena_0.8.8.orig/code/tools/lcc/src/string.c0000644000175000017500000001033511656310262020302 0ustar smcvsmcv#include "c.h" static struct string { char *str; int len; struct string *link; } *buckets[1024]; static int scatter[] = { /* map characters to random values */ 2078917053, 143302914, 1027100827, 1953210302, 755253631, 2002600785, 1405390230, 45248011, 1099951567, 433832350, 2018585307, 438263339, 813528929, 1703199216, 618906479, 573714703, 766270699, 275680090, 1510320440, 1583583926, 1723401032, 1965443329, 1098183682, 1636505764, 980071615, 1011597961, 643279273, 1315461275, 157584038, 1069844923, 471560540, 89017443, 1213147837, 1498661368, 2042227746, 1968401469, 1353778505, 1300134328, 2013649480, 306246424, 1733966678, 1884751139, 744509763, 400011959, 1440466707, 1363416242, 973726663, 59253759, 1639096332, 336563455, 1642837685, 1215013716, 154523136, 593537720, 704035832, 1134594751, 1605135681, 1347315106, 302572379, 1762719719, 269676381, 774132919, 1851737163, 1482824219, 125310639, 1746481261, 1303742040, 1479089144, 899131941, 1169907872, 1785335569, 485614972, 907175364, 382361684, 885626931, 200158423, 1745777927, 1859353594, 259412182, 1237390611, 48433401, 1902249868, 304920680, 202956538, 348303940, 1008956512, 1337551289, 1953439621, 208787970, 1640123668, 1568675693, 478464352, 266772940, 1272929208, 1961288571, 392083579, 871926821, 1117546963, 1871172724, 1771058762, 139971187, 1509024645, 109190086, 1047146551, 1891386329, 994817018, 1247304975, 1489680608, 706686964, 1506717157, 579587572, 755120366, 1261483377, 884508252, 958076904, 1609787317, 1893464764, 148144545, 1415743291, 2102252735, 1788268214, 836935336, 433233439, 2055041154, 2109864544, 247038362, 299641085, 834307717, 1364585325, 23330161, 457882831, 1504556512, 1532354806, 567072918, 404219416, 1276257488, 1561889936, 1651524391, 618454448, 121093252, 1010757900, 1198042020, 876213618, 124757630, 2082550272, 1834290522, 1734544947, 1828531389, 1982435068, 1002804590, 1783300476, 1623219634, 1839739926, 69050267, 1530777140, 1802120822, 316088629, 1830418225, 488944891, 1680673954, 1853748387, 946827723, 1037746818, 1238619545, 1513900641, 1441966234, 367393385, 928306929, 946006977, 985847834, 1049400181, 1956764878, 36406206, 1925613800, 2081522508, 2118956479, 1612420674, 1668583807, 1800004220, 1447372094, 523904750, 1435821048, 923108080, 216161028, 1504871315, 306401572, 2018281851, 1820959944, 2136819798, 359743094, 1354150250, 1843084537, 1306570817, 244413420, 934220434, 672987810, 1686379655, 1301613820, 1601294739, 484902984, 139978006, 503211273, 294184214, 176384212, 281341425, 228223074, 147857043, 1893762099, 1896806882, 1947861263, 1193650546, 273227984, 1236198663, 2116758626, 489389012, 593586330, 275676551, 360187215, 267062626, 265012701, 719930310, 1621212876, 2108097238, 2026501127, 1865626297, 894834024, 552005290, 1404522304, 48964196, 5816381, 1889425288, 188942202, 509027654, 36125855, 365326415, 790369079, 264348929, 513183458, 536647531, 13672163, 313561074, 1730298077, 286900147, 1549759737, 1699573055, 776289160, 2143346068, 1975249606, 1136476375, 262925046, 92778659, 1856406685, 1884137923, 53392249, 1735424165, 1602280572 }; char *string(const char *str) { const char *s; for (s = str; *s; s++) ; return stringn(str, s - str); } char *stringd(long n) { char str[25], *s = str + sizeof (str); unsigned long m; if (n == LONG_MIN) m = (unsigned long)LONG_MAX + 1; else if (n < 0) m = -n; else m = n; do *--s = m%10 + '0'; while ((m /= 10) != 0); if (n < 0) *--s = '-'; return stringn(s, str + sizeof (str) - s); } char *stringn(const char *str, int len) { int i; unsigned int h; const char *end; struct string *p; assert(str); for (h = 0, i = len, end = str; i > 0; i--) h = (h<<1) + scatter[*(unsigned char *)end++]; h &= NELEMS(buckets)-1; for (p = buckets[h]; p; p = p->link) if (len == p->len) { const char *s1 = str; char *s2 = p->str; do { if (s1 == end) return p->str; } while (*s1++ == *s2++); } { static char *next, *strlimit; if (len + 1 >= strlimit - next) { int n = len + 4*1024; next = allocate(n, PERM); strlimit = next + n; } NEW(p, PERM); p->len = len; for (p->str = next; str < end; ) *next++ = *str++; *next++ = 0; p->link = buckets[h]; buckets[h] = p; return p->str; } } openarena_0.8.8.orig/code/tools/lcc/src/decl.c0000644000175000017500000007512311656310262017711 0ustar smcvsmcv#include "c.h" #define add(x,n) (x > inttype->u.sym->u.limits.max.i-(n) ? (overflow=1,x) : x+(n)) #define chkoverflow(x,n) ((void)add(x,n)) #define bits2bytes(n) (((n) + 7)/8) static int regcount; static List autos, registers; Symbol cfunc; /* current function */ Symbol retv; /* return value location for structs */ static void checkref(Symbol, void *); static Symbol dclglobal(int, char *, Type, Coordinate *); static Symbol dcllocal(int, char *, Type, Coordinate *); static Symbol dclparam(int, char *, Type, Coordinate *); static Type dclr(Type, char **, Symbol **, int); static Type dclr1(char **, Symbol **, int); static void decl(Symbol (*)(int, char *, Type, Coordinate *)); extern void doconst(Symbol, void *); static void doglobal(Symbol, void *); static void doextern(Symbol, void *); static void exitparams(Symbol []); static void fields(Type); static void funcdefn(int, char *, Type, Symbol [], Coordinate); static void initglobal(Symbol, int); static void oldparam(Symbol, void *); static Symbol *parameters(Type); static Type specifier(int *); static Type structdcl(int); static Type tnode(int, Type); void program(void) { int n; level = GLOBAL; for (n = 0; t != EOI; n++) if (kind[t] == CHAR || kind[t] == STATIC || t == ID || t == '*' || t == '(') { decl(dclglobal); deallocate(STMT); if (!(glevel >= 3 || xref)) deallocate(FUNC); } else if (t == ';') { warning("empty declaration\n"); t = gettok(); } else { error("unrecognized declaration\n"); t = gettok(); } if (n == 0) warning("empty input file\n"); } static Type specifier(int *sclass) { int cls, cons, sign, size, type, vol; Type ty = NULL; cls = vol = cons = sign = size = type = 0; if (sclass == NULL) cls = AUTO; for (;;) { int *p, tt = t; switch (t) { case AUTO: case REGISTER: if (level <= GLOBAL && cls == 0) error("invalid use of `%k'\n", t); p = &cls; t = gettok(); break; case STATIC: case EXTERN: case TYPEDEF: p = &cls; t = gettok(); break; case CONST: p = &cons; t = gettok(); break; case VOLATILE: p = &vol; t = gettok(); break; case SIGNED: case UNSIGNED: p = &sign; t = gettok(); break; case LONG: if (size == LONG) { size = 0; tt = LONG+LONG; } p = &size; t = gettok(); break; case SHORT: p = &size; t = gettok(); break; case VOID: case CHAR: case INT: case FLOAT: case DOUBLE: p = &type; ty = tsym->type; t = gettok(); break; case ENUM: p = &type; ty = enumdcl(); break; case STRUCT: case UNION: p = &type; ty = structdcl(t); break; case ID: if (istypename(t, tsym) && type == 0 && sign == 0 && size == 0) { use(tsym, src); ty = tsym->type; if (isqual(ty) && ty->size != ty->type->size) { ty = unqual(ty); if (isconst(tsym->type)) ty = qual(CONST, ty); if (isvolatile(tsym->type)) ty = qual(VOLATILE, ty); tsym->type = ty; } p = &type; t = gettok(); } else p = NULL; break; default: p = NULL; } if (p == NULL) break; if (*p) error("invalid use of `%k'\n", tt); *p = tt; } if (sclass) *sclass = cls; if (type == 0) { type = INT; ty = inttype; } if ((size == SHORT && type != INT) || (size == LONG+LONG && type != INT) || (size == LONG && type != INT && type != DOUBLE) || (sign && type != INT && type != CHAR)) error("invalid type specification\n"); if (type == CHAR && sign) ty = sign == UNSIGNED ? unsignedchar : signedchar; else if (size == SHORT) ty = sign == UNSIGNED ? unsignedshort : shorttype; else if (size == LONG && type == DOUBLE) ty = longdouble; else if (size == LONG+LONG) { ty = sign == UNSIGNED ? unsignedlonglong : longlong; if (Aflag >= 1) warning("`%t' is a non-ANSI type\n", ty); } else if (size == LONG) ty = sign == UNSIGNED ? unsignedlong : longtype; else if (sign == UNSIGNED && type == INT) ty = unsignedtype; if (cons == CONST) ty = qual(CONST, ty); if (vol == VOLATILE) ty = qual(VOLATILE, ty); return ty; } static void decl(Symbol (*dcl)(int, char *, Type, Coordinate *)) { int sclass; Type ty, ty1; static char stop[] = { CHAR, STATIC, ID, 0 }; ty = specifier(&sclass); if (t == ID || t == '*' || t == '(' || t == '[') { char *id; Coordinate pos; id = NULL; pos = src; if (level == GLOBAL) { Symbol *params = NULL; ty1 = dclr(ty, &id, ¶ms, 0); if (params && id && isfunc(ty1) && (t == '{' || istypename(t, tsym) || (kind[t] == STATIC && t != TYPEDEF))) { if (sclass == TYPEDEF) { error("invalid use of `typedef'\n"); sclass = EXTERN; } if (ty1->u.f.oldstyle) exitscope(); funcdefn(sclass, id, ty1, params, pos); return; } else if (params) exitparams(params); } else ty1 = dclr(ty, &id, NULL, 0); for (;;) { if (Aflag >= 1 && !hasproto(ty1)) warning("missing prototype\n"); if (id == NULL) error("missing identifier\n"); else if (sclass == TYPEDEF) { Symbol p = lookup(id, identifiers); if (p && p->scope == level) error("redeclaration of `%s'\n", id); p = install(id, &identifiers, level, level < LOCAL ? PERM : FUNC); p->type = ty1; p->sclass = TYPEDEF; p->src = pos; } else (void)(*dcl)(sclass, id, ty1, &pos); if (t != ',') break; t = gettok(); id = NULL; pos = src; ty1 = dclr(ty, &id, NULL, 0); } } else if (ty == NULL || !(isenum(ty) || (isstruct(ty) && (*unqual(ty)->u.sym->name < '1' || *unqual(ty)->u.sym->name > '9')))) error("empty declaration\n"); test(';', stop); } static Symbol dclglobal(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p; if (sclass == 0) sclass = AUTO; else if (sclass != EXTERN && sclass != STATIC) { error("invalid storage class `%k' for `%t %s'\n", sclass, ty, id); sclass = AUTO; } p = lookup(id, identifiers); if (p && p->scope == GLOBAL) { if (p->sclass != TYPEDEF && eqtype(ty, p->type, 1)) ty = compose(ty, p->type); else error("redeclaration of `%s' previously declared at %w\n", p->name, &p->src); if (!isfunc(ty) && p->defined && t == '=') error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); if ((p->sclass == EXTERN && sclass == STATIC) || (p->sclass == STATIC && sclass == AUTO) || (p->sclass == AUTO && sclass == STATIC)) warning("inconsistent linkage for `%s' previously declared at %w\n", p->name, &p->src); } if (p == NULL || p->scope != GLOBAL) { Symbol q = lookup(id, externals); if (q) { if (sclass == STATIC || !eqtype(ty, q->type, 1)) warning("declaration of `%s' does not match previous declaration at %w\n", id, &q->src); p = relocate(id, externals, globals); p->sclass = sclass; } else { p = install(id, &globals, GLOBAL, PERM); p->sclass = sclass; (*IR->defsymbol)(p); } if (p->sclass != STATIC) { static int nglobals; nglobals++; if (Aflag >= 2 && nglobals == 512) warning("more than 511 external identifiers\n"); } } else if (p->sclass == EXTERN) p->sclass = sclass; p->type = ty; p->src = *pos; if (t == '=' && isfunc(p->type)) { error("illegal initialization for `%s'\n", p->name); t = gettok(); initializer(p->type, 0); } else if (t == '=') { initglobal(p, 0); if (glevel > 0 && IR->stabsym) { (*IR->stabsym)(p); swtoseg(p->u.seg); } } else if (p->sclass == STATIC && !isfunc(p->type) && p->type->size == 0) error("undefined size for `%t %s'\n", p->type, p->name); return p; } static void initglobal(Symbol p, int flag) { Type ty; if (t == '=' || flag) { if (p->sclass == STATIC) { for (ty = p->type; isarray(ty); ty = ty->type) ; defglobal(p, isconst(ty) ? LIT : DATA); } else defglobal(p, DATA); if (t == '=') t = gettok(); ty = initializer(p->type, 0); if (isarray(p->type) && p->type->size == 0) p->type = ty; if (p->sclass == EXTERN) p->sclass = AUTO; } } void defglobal(Symbol p, int seg) { p->u.seg = seg; swtoseg(p->u.seg); if (p->sclass != STATIC) (*IR->export)(p); (*IR->global)(p); p->defined = 1; } static Type dclr(Type basety, char **id, Symbol **params, int abstract) { Type ty = dclr1(id, params, abstract); for ( ; ty; ty = ty->type) switch (ty->op) { case POINTER: basety = ptr(basety); break; case FUNCTION: basety = func(basety, ty->u.f.proto, ty->u.f.oldstyle); break; case ARRAY: basety = array(basety, ty->size, 0); break; case CONST: case VOLATILE: basety = qual(ty->op, basety); break; default: assert(0); } if (Aflag >= 2 && basety->size > 32767) warning("more than 32767 bytes in `%t'\n", basety); return basety; } static Type tnode(int op, Type type) { Type ty; NEW0(ty, STMT); ty->op = op; ty->type = type; return ty; } static Type dclr1(char **id, Symbol **params, int abstract) { Type ty = NULL; switch (t) { case ID: if (id) *id = token; else error("extraneous identifier `%s'\n", token); t = gettok(); break; case '*': t = gettok(); if (t == CONST || t == VOLATILE) { Type ty1; ty1 = ty = tnode(t, NULL); while ((t = gettok()) == CONST || t == VOLATILE) ty1 = tnode(t, ty1); ty->type = dclr1(id, params, abstract); ty = ty1; } else ty = dclr1(id, params, abstract); ty = tnode(POINTER, ty); break; case '(': t = gettok(); if (abstract && (t == REGISTER || istypename(t, tsym) || t == ')')) { Symbol *args; ty = tnode(FUNCTION, ty); enterscope(); if (level > PARAM) enterscope(); args = parameters(ty); exitparams(args); } else { ty = dclr1(id, params, abstract); expect(')'); if (abstract && ty == NULL && (id == NULL || *id == NULL)) return tnode(FUNCTION, NULL); } break; case '[': break; default: return ty; } while (t == '(' || t == '[') switch (t) { case '(': t = gettok(); { Symbol *args; ty = tnode(FUNCTION, ty); enterscope(); if (level > PARAM) enterscope(); args = parameters(ty); if (params && *params == NULL) *params = args; else exitparams(args); } break; case '[': t = gettok(); { int n = 0; if (kind[t] == ID) { n = intexpr(']', 1); if (n <= 0) { error("`%d' is an illegal array size\n", n); n = 1; } } else expect(']'); ty = tnode(ARRAY, ty); ty->size = n; } break; default: assert(0); } return ty; } static Symbol *parameters(Type fty) { List list = NULL; Symbol *params; if (kind[t] == STATIC || istypename(t, tsym)) { int n = 0; Type ty1 = NULL; for (;;) { Type ty; int sclass = 0; char *id = NULL; if (ty1 && t == ELLIPSIS) { static struct symbol sentinel; if (sentinel.type == NULL) { sentinel.type = voidtype; sentinel.defined = 1; } if (ty1 == voidtype) error("illegal formal parameter types\n"); list = append(&sentinel, list); t = gettok(); break; } if (!istypename(t, tsym) && t != REGISTER) error("missing parameter type\n"); n++; ty = dclr(specifier(&sclass), &id, NULL, 1); if ( (ty == voidtype && (ty1 || id)) || ty1 == voidtype) error("illegal formal parameter types\n"); if (id == NULL) id = stringd(n); if (ty != voidtype) list = append(dclparam(sclass, id, ty, &src), list); if (Aflag >= 1 && !hasproto(ty)) warning("missing prototype\n"); if (ty1 == NULL) ty1 = ty; if (t != ',') break; t = gettok(); } fty->u.f.proto = newarray(length(list) + 1, sizeof (Type *), PERM); params = ltov(&list, FUNC); for (n = 0; params[n]; n++) fty->u.f.proto[n] = params[n]->type; fty->u.f.proto[n] = NULL; fty->u.f.oldstyle = 0; } else { if (t == ID) for (;;) { Symbol p; if (t != ID) { error("expecting an identifier\n"); break; } p = dclparam(0, token, inttype, &src); p->defined = 0; list = append(p, list); t = gettok(); if (t != ',') break; t = gettok(); } params = ltov(&list, FUNC); fty->u.f.proto = NULL; fty->u.f.oldstyle = 1; } if (t != ')') { static char stop[] = { CHAR, STATIC, IF, ')', 0 }; expect(')'); skipto('{', stop); } if (t == ')') t = gettok(); return params; } static void exitparams(Symbol params[]) { assert(params); if (params[0] && !params[0]->defined) error("extraneous old-style parameter list\n"); if (level > PARAM) exitscope(); exitscope(); } static Symbol dclparam(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p; if (isfunc(ty)) ty = ptr(ty); else if (isarray(ty)) ty = atop(ty); if (sclass == 0) sclass = AUTO; else if (sclass != REGISTER) { error("invalid storage class `%k' for `%t%s\n", sclass, ty, stringf(id ? " %s'" : "' parameter", id)); sclass = AUTO; } else if (isvolatile(ty) || isstruct(ty)) { warning("register declaration ignored for `%t%s\n", ty, stringf(id ? " %s'" : "' parameter", id)); sclass = AUTO; } p = lookup(id, identifiers); if (p && p->scope == level) error("duplicate declaration for `%s' previously declared at %w\n", id, &p->src); else p = install(id, &identifiers, level, FUNC); p->sclass = sclass; p->src = *pos; p->type = ty; p->defined = 1; if (t == '=') { error("illegal initialization for parameter `%s'\n", id); t = gettok(); (void)expr1(0); } return p; } static Type structdcl(int op) { char *tag; Type ty; Symbol p; Coordinate pos; t = gettok(); pos = src; if (t == ID) { tag = token; t = gettok(); } else tag = ""; if (t == '{') { static char stop[] = { IF, ',', 0 }; ty = newstruct(op, tag); ty->u.sym->src = pos; ty->u.sym->defined = 1; t = gettok(); if (istypename(t, tsym)) fields(ty); else error("invalid %k field declarations\n", op); test('}', stop); } else if (*tag && (p = lookup(tag, types)) != NULL && p->type->op == op) { ty = p->type; if (t == ';' && p->scope < level) ty = newstruct(op, tag); } else { if (*tag == 0) error("missing %k tag\n", op); ty = newstruct(op, tag); } if (*tag && xref) use(ty->u.sym, pos); return ty; } static void fields(Type ty) { { int n = 0; while (istypename(t, tsym)) { static char stop[] = { IF, CHAR, '}', 0 }; Type ty1 = specifier(NULL); for (;;) { Field p; char *id = NULL; Type fty = dclr(ty1, &id, NULL, 0); p = newfield(id, ty, fty); if (Aflag >= 1 && !hasproto(p->type)) warning("missing prototype\n"); if (t == ':') { if (unqual(p->type) != inttype && unqual(p->type) != unsignedtype) { error("`%t' is an illegal bit-field type\n", p->type); p->type = inttype; } t = gettok(); p->bitsize = intexpr(0, 0); if (p->bitsize > 8*inttype->size || p->bitsize < 0) { error("`%d' is an illegal bit-field size\n", p->bitsize); p->bitsize = 8*inttype->size; } else if (p->bitsize == 0 && id) { warning("extraneous 0-width bit field `%t %s' ignored\n", p->type, id); p->name = stringd(genlabel(1)); } p->lsb = 1; } else { if (id == NULL) error("field name missing\n"); else if (isfunc(p->type)) error("`%t' is an illegal field type\n", p->type); else if (p->type->size == 0) error("undefined size for field `%t %s'\n", p->type, id); } if (isconst(p->type)) ty->u.sym->u.s.cfields = 1; if (isvolatile(p->type)) ty->u.sym->u.s.vfields = 1; n++; if (Aflag >= 2 && n == 128) warning("more than 127 fields in `%t'\n", ty); if (t != ',') break; t = gettok(); } test(';', stop); } } { int bits = 0, off = 0, overflow = 0; Field p, *q = &ty->u.sym->u.s.flist; ty->align = IR->structmetric.align; for (p = *q; p; p = p->link) { int a = p->type->align ? p->type->align : 1; if (p->lsb) a = unsignedtype->align; if (ty->op == UNION) off = bits = 0; else if (p->bitsize == 0 || bits == 0 || bits - 1 + p->bitsize > 8*unsignedtype->size) { off = add(off, bits2bytes(bits-1)); bits = 0; chkoverflow(off, a - 1); off = roundup(off, a); } if (a > ty->align) ty->align = a; p->offset = off; if (p->lsb) { if (bits == 0) bits = 1; if (IR->little_endian) p->lsb = bits; else p->lsb = 8*unsignedtype->size - bits + 1 - p->bitsize + 1; bits += p->bitsize; } else off = add(off, p->type->size); if (off + bits2bytes(bits-1) > ty->size) ty->size = off + bits2bytes(bits-1); if (p->name == NULL || !('1' <= *p->name && *p->name <= '9')) { *q = p; q = &p->link; } } *q = NULL; chkoverflow(ty->size, ty->align - 1); ty->size = roundup(ty->size, ty->align); if (overflow) { error("size of `%t' exceeds %d bytes\n", ty, inttype->u.sym->u.limits.max.i); ty->size = inttype->u.sym->u.limits.max.i&(~(ty->align - 1)); } } } static void funcdefn(int sclass, char *id, Type ty, Symbol params[], Coordinate pt) { int i, n; Symbol *callee, *caller, p; Type rty = freturn(ty); if (isstruct(rty) && rty->size == 0) error("illegal use of incomplete type `%t'\n", rty); for (n = 0; params[n]; n++) ; if (n > 0 && params[n-1]->name == NULL) params[--n] = NULL; if (Aflag >= 2 && n > 31) warning("more than 31 parameters in function `%s'\n", id); if (ty->u.f.oldstyle) { if (Aflag >= 1) warning("old-style function definition for `%s'\n", id); caller = params; callee = newarray(n + 1, sizeof *callee, FUNC); memcpy(callee, caller, (n+1)*sizeof *callee); enterscope(); assert(level == PARAM); while (kind[t] == STATIC || istypename(t, tsym)) decl(dclparam); foreach(identifiers, PARAM, oldparam, callee); for (i = 0; (p = callee[i]) != NULL; i++) { if (!p->defined) callee[i] = dclparam(0, p->name, inttype, &p->src); *caller[i] = *p; caller[i]->sclass = AUTO; caller[i]->type = promote(p->type); } p = lookup(id, identifiers); if (p && p->scope == GLOBAL && isfunc(p->type) && p->type->u.f.proto) { Type *proto = p->type->u.f.proto; for (i = 0; caller[i] && proto[i]; i++) { Type ty = unqual(proto[i]); if (eqtype(isenum(ty) ? ty->type : ty, unqual(caller[i]->type), 1) == 0) break; else if (isenum(ty) && !isenum(unqual(caller[i]->type))) warning("compatibility of `%t' and `%t' is compiler dependent\n", proto[i], caller[i]->type); } if (proto[i] || caller[i]) error("conflicting argument declarations for function `%s'\n", id); } else { Type *proto = newarray(n + 1, sizeof *proto, PERM); if (Aflag >= 1) warning("missing prototype for `%s'\n", id); for (i = 0; i < n; i++) proto[i] = caller[i]->type; proto[i] = NULL; ty = func(rty, proto, 1); } } else { callee = params; caller = newarray(n + 1, sizeof *caller, FUNC); for (i = 0; (p = callee[i]) != NULL && p->name; i++) { NEW(caller[i], FUNC); *caller[i] = *p; if (isint(p->type)) caller[i]->type = promote(p->type); caller[i]->sclass = AUTO; if ('1' <= *p->name && *p->name <= '9') error("missing name for parameter %d to function `%s'\n", i + 1, id); } caller[i] = NULL; } for (i = 0; (p = callee[i]) != NULL; i++) if (p->type->size == 0) { error("undefined size for parameter `%t %s'\n", p->type, p->name); caller[i]->type = p->type = inttype; } if (Aflag >= 2 && sclass != STATIC && strcmp(id, "main") == 0) { if (ty->u.f.oldstyle) warning("`%t %s()' is a non-ANSI definition\n", rty, id); else if (!(rty == inttype && ((n == 0 && callee[0] == NULL) || (n == 2 && callee[0]->type == inttype && isptr(callee[1]->type) && callee[1]->type->type == charptype && !variadic(ty))))) warning("`%s' is a non-ANSI definition\n", typestring(ty, id)); } p = lookup(id, identifiers); if (p && isfunc(p->type) && p->defined) error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); cfunc = dclglobal(sclass, id, ty, &pt); cfunc->u.f.label = genlabel(1); cfunc->u.f.callee = callee; cfunc->u.f.pt = src; cfunc->defined = 1; if (xref) use(cfunc, cfunc->src); if (Pflag) printproto(cfunc, cfunc->u.f.callee); if (ncalled >= 0) ncalled = findfunc(cfunc->name, pt.file); labels = table(NULL, LABELS); stmtlabs = table(NULL, LABELS); refinc = 1.0; regcount = 0; codelist = &codehead; codelist->next = NULL; if (!IR->wants_callb && isstruct(rty)) retv = genident(AUTO, ptr(rty), PARAM); compound(0, NULL, 0); { Code cp; for (cp = codelist; cp->kind < Label; cp = cp->prev) ; if (cp->kind != Jump) { if (rty != voidtype) { warning("missing return value\n"); retcode(cnsttree(inttype, 0L)); } else retcode(NULL); } } definelab(cfunc->u.f.label); if (events.exit) apply(events.exit, cfunc, NULL); walk(NULL, 0, 0); exitscope(); assert(level == PARAM); foreach(identifiers, level, checkref, NULL); if (!IR->wants_callb && isstruct(rty)) { Symbol *a; a = newarray(n + 2, sizeof *a, FUNC); a[0] = retv; memcpy(&a[1], callee, (n+1)*sizeof *callee); callee = a; a = newarray(n + 2, sizeof *a, FUNC); NEW(a[0], FUNC); *a[0] = *retv; memcpy(&a[1], caller, (n+1)*sizeof *callee); caller = a; } if (!IR->wants_argb) for (i = 0; caller[i]; i++) if (isstruct(caller[i]->type)) { caller[i]->type = ptr(caller[i]->type); callee[i]->type = ptr(callee[i]->type); caller[i]->structarg = callee[i]->structarg = 1; } if (glevel > 1) for (i = 0; callee[i]; i++) callee[i]->sclass = AUTO; if (cfunc->sclass != STATIC) (*IR->export)(cfunc); if (glevel && IR->stabsym) { swtoseg(CODE); (*IR->stabsym)(cfunc); } swtoseg(CODE); (*IR->function)(cfunc, caller, callee, cfunc->u.f.ncalls); if (glevel && IR->stabfend) (*IR->stabfend)(cfunc, lineno); foreach(stmtlabs, LABELS, checklab, NULL); exitscope(); expect('}'); labels = stmtlabs = NULL; retv = NULL; cfunc = NULL; } static void oldparam(Symbol p, void *cl) { int i; Symbol *callee = cl; for (i = 0; callee[i]; i++) if (p->name == callee[i]->name) { callee[i] = p; return; } error("declared parameter `%s' is missing\n", p->name); } void compound(int loop, struct swtch *swp, int lev) { Code cp; int nregs; walk(NULL, 0, 0); cp = code(Blockbeg); enterscope(); assert(level >= LOCAL); if (level == LOCAL && events.entry) apply(events.entry, cfunc, NULL); definept(NULL); expect('{'); autos = registers = NULL; if (level == LOCAL && IR->wants_callb && isstruct(freturn(cfunc->type))) { retv = genident(AUTO, ptr(freturn(cfunc->type)), level); retv->defined = 1; retv->ref = 1; registers = append(retv, registers); } while (kind[t] == CHAR || kind[t] == STATIC || (istypename(t, tsym) && getchr() != ':')) decl(dcllocal); { int i; Symbol *a = ltov(&autos, STMT); nregs = length(registers); for (i = 0; a[i]; i++) registers = append(a[i], registers); cp->u.block.locals = ltov(®isters, FUNC); } if (events.blockentry) apply(events.blockentry, cp->u.block.locals, NULL); while (kind[t] == IF || kind[t] == ID) statement(loop, swp, lev); walk(NULL, 0, 0); foreach(identifiers, level, checkref, NULL); { int i = nregs, j; Symbol p; for ( ; (p = cp->u.block.locals[i]) != NULL; i++) { for (j = i; j > nregs && cp->u.block.locals[j-1]->ref < p->ref; j--) cp->u.block.locals[j] = cp->u.block.locals[j-1]; cp->u.block.locals[j] = p; } } if (events.blockexit) apply(events.blockexit, cp->u.block.locals, NULL); cp->u.block.level = level; cp->u.block.identifiers = identifiers; cp->u.block.types = types; code(Blockend)->u.begin = cp; if (reachable(Gen)) definept(NULL); if (level > LOCAL) { exitscope(); expect('}'); } } static void checkref(Symbol p, void *cl) { if (p->scope >= PARAM && (isvolatile(p->type) || isfunc(p->type))) p->addressed = 1; if (Aflag >= 2 && p->defined && p->ref == 0) { if (p->sclass == STATIC) warning("static `%t %s' is not referenced\n", p->type, p->name); else if (p->scope == PARAM) warning("parameter `%t %s' is not referenced\n", p->type, p->name); else if (p->scope >= LOCAL && p->sclass != EXTERN) warning("local `%t %s' is not referenced\n", p->type, p->name); } if (p->sclass == AUTO && ((p->scope == PARAM && regcount == 0) || p->scope >= LOCAL) && !p->addressed && isscalar(p->type) && p->ref >= 3.0) p->sclass = REGISTER; if (level == GLOBAL && p->sclass == STATIC && !p->defined && isfunc(p->type) && p->ref) error("undefined static `%t %s'\n", p->type, p->name); assert(!(level == GLOBAL && p->sclass == STATIC && !p->defined && !isfunc(p->type))); } static Symbol dcllocal(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p, q; if (sclass == 0) sclass = isfunc(ty) ? EXTERN : AUTO; else if (isfunc(ty) && sclass != EXTERN) { error("invalid storage class `%k' for `%t %s'\n", sclass, ty, id); sclass = EXTERN; } else if (sclass == REGISTER && (isvolatile(ty) || isstruct(ty) || isarray(ty))) { warning("register declaration ignored for `%t %s'\n", ty, id); sclass = AUTO; } q = lookup(id, identifiers); if ((q && q->scope >= level) || (q && q->scope == PARAM && level == LOCAL)) { if (sclass == EXTERN && q->sclass == EXTERN && eqtype(q->type, ty, 1)) ty = compose(ty, q->type); else error("redeclaration of `%s' previously declared at %w\n", q->name, &q->src); } assert(level >= LOCAL); p = install(id, &identifiers, level, sclass == STATIC || sclass == EXTERN ? PERM : FUNC); p->type = ty; p->sclass = sclass; p->src = *pos; switch (sclass) { case EXTERN: q = lookup(id, globals); if (q == NULL || q->sclass == TYPEDEF || q->sclass == ENUM) { q = lookup(id, externals); if (q == NULL) { q = install(p->name, &externals, GLOBAL, PERM); q->type = p->type; q->sclass = EXTERN; q->src = src; (*IR->defsymbol)(q); } } if (!eqtype(p->type, q->type, 1)) warning("declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src); p->u.alias = q; break; case STATIC: (*IR->defsymbol)(p); initglobal(p, 0); if (!p->defined) { if (p->type->size > 0) { defglobal(p, BSS); (*IR->space)(p->type->size); } else error("undefined size for `%t %s'\n", p->type, p->name); } p->defined = 1; break; case REGISTER: registers = append(p, registers); regcount++; p->defined = 1; break; case AUTO: autos = append(p, autos); p->defined = 1; break; default: assert(0); } if (t == '=') { Tree e; if (sclass == EXTERN) error("illegal initialization of `extern %s'\n", id); t = gettok(); definept(NULL); if (isscalar(p->type) || (isstruct(p->type) && t != '{')) { if (t == '{') { t = gettok(); e = expr1(0); expect('}'); } else e = expr1(0); } else { Symbol t1; Type ty = p->type, ty1 = ty; while (isarray(ty1)) ty1 = ty1->type; if (!isconst(ty) && (!isarray(ty) || !isconst(ty1))) ty = qual(CONST, ty); t1 = genident(STATIC, ty, GLOBAL); initglobal(t1, 1); if (isarray(p->type) && p->type->size == 0 && t1->type->size > 0) p->type = array(p->type->type, t1->type->size/t1->type->type->size, 0); e = idtree(t1); } walk(root(asgn(p, e)), 0, 0); p->ref = 1; } if (!isfunc(p->type) && p->defined && p->type->size <= 0) error("undefined size for `%t %s'\n", p->type, id); return p; } void finalize(void) { foreach(externals, GLOBAL, doextern, NULL); foreach(identifiers, GLOBAL, doglobal, NULL); foreach(identifiers, GLOBAL, checkref, NULL); foreach(constants, CONSTANTS, doconst, NULL); } static void doextern(Symbol p, void *cl) { (*IR->import)(p); } static void doglobal(Symbol p, void *cl) { if (!p->defined && (p->sclass == EXTERN || (isfunc(p->type) && p->sclass == AUTO))) (*IR->import)(p); else if (!p->defined && !isfunc(p->type) && (p->sclass == AUTO || p->sclass == STATIC)) { if (isarray(p->type) && p->type->size == 0 && p->type->type->size > 0) p->type = array(p->type->type, 1, 0); if (p->type->size > 0) { defglobal(p, BSS); (*IR->space)(p->type->size); if (glevel > 0 && IR->stabsym) (*IR->stabsym)(p); } else error("undefined size for `%t %s'\n", p->type, p->name); p->defined = 1; } if (Pflag && !isfunc(p->type) && !p->generated && p->sclass != EXTERN) printdecl(p, p->type); } void doconst(Symbol p, void *cl) { if (p->u.c.loc) { assert(p->u.c.loc->u.seg == 0); defglobal(p->u.c.loc, LIT); if (isarray(p->type) && p->type->type == widechar) { unsigned int *s = p->u.c.v.p; int n = p->type->size/widechar->size; while (n-- > 0) { Value v; v.u = *s++; (*IR->defconst)(widechar->op, widechar->size, v); } } else if (isarray(p->type)) (*IR->defstring)(p->type->size, p->u.c.v.p); else (*IR->defconst)(p->type->op, p->type->size, p->u.c.v); p->u.c.loc = NULL; } } void checklab(Symbol p, void *cl) { if (!p->defined) error("undefined label `%s'\n", p->name); p->defined = 1; } Type enumdcl(void) { char *tag; Type ty; Symbol p = {0}; Coordinate pos; t = gettok(); pos = src; if (t == ID) { tag = token; t = gettok(); } else tag = ""; if (t == '{') { static char follow[] = { IF, 0 }; int n = 0; long k = -1; List idlist = 0; ty = newstruct(ENUM, tag); t = gettok(); if (t != ID) error("expecting an enumerator identifier\n"); while (t == ID) { char *id = token; Coordinate s; if (tsym && tsym->scope == level) error("redeclaration of `%s' previously declared at %w\n", token, &tsym->src); s = src; t = gettok(); if (t == '=') { t = gettok(); k = intexpr(0, 0); } else { if (k == inttype->u.sym->u.limits.max.i) error("overflow in value for enumeration constant `%s'\n", id); k++; } p = install(id, &identifiers, level, level < LOCAL ? PERM : FUNC); p->src = s; p->type = ty; p->sclass = ENUM; p->u.value = k; idlist = append(p, idlist); n++; if (Aflag >= 2 && n == 128) warning("more than 127 enumeration constants in `%t'\n", ty); if (t != ',') break; t = gettok(); if (Aflag >= 2 && t == '}') warning("non-ANSI trailing comma in enumerator list\n"); } test('}', follow); ty->type = inttype; ty->size = ty->type->size; ty->align = ty->type->align; ty->u.sym->u.idlist = ltov(&idlist, PERM); ty->u.sym->defined = 1; } else if ((p = lookup(tag, types)) != NULL && p->type->op == ENUM) { ty = p->type; if (t == ';') error("empty declaration\n"); } else { error("unknown enumeration `%s'\n", tag); ty = newstruct(ENUM, tag); ty->type = inttype; } if (*tag && xref) use(p, pos); return ty; } Type typename(void) { Type ty = specifier(NULL); if (t == '*' || t == '(' || t == '[') { ty = dclr(ty, NULL, NULL, 1); if (Aflag >= 1 && !hasproto(ty)) warning("missing prototype\n"); } return ty; } openarena_0.8.8.orig/code/tools/lcc/src/expr.c0000644000175000017500000004363211656310263017761 0ustar smcvsmcv#include "c.h" static char prec[] = { #define xx(a,b,c,d,e,f,g) c, #define yy(a,b,c,d,e,f,g) c, #include "token.h" }; static int oper[] = { #define xx(a,b,c,d,e,f,g) d, #define yy(a,b,c,d,e,f,g) d, #include "token.h" }; float refinc = 1.0; static Tree expr2(void); static Tree expr3(int); static Tree nullcheck(Tree); static Tree postfix(Tree); static Tree unary(void); static Tree primary(void); static Type super(Type ty); static Type super(Type ty) { switch (ty->op) { case INT: if (ty->size < inttype->size) return inttype; break; case UNSIGNED: if (ty->size < unsignedtype->size) return unsignedtype; break; case POINTER: return unsignedptr; } return ty; } Tree expr(int tok) { static char stop[] = { IF, ID, '}', 0 }; Tree p = expr1(0); while (t == ',') { Tree q; t = gettok(); q = pointer(expr1(0)); p = tree(RIGHT, q->type, root(value(p)), q); } if (tok) test(tok, stop); return p; } Tree expr0(int tok) { return root(expr(tok)); } Tree expr1(int tok) { static char stop[] = { IF, ID, 0 }; Tree p = expr2(); if (t == '=' || (prec[t] >= 6 && prec[t] <= 8) || (prec[t] >= 11 && prec[t] <= 13)) { int op = t; t = gettok(); if (oper[op] == ASGN) p = asgntree(ASGN, p, value(expr1(0))); else { expect('='); p = incr(op, p, expr1(0)); } } if (tok) test(tok, stop); return p; } Tree incr(int op, Tree v, Tree e) { return asgntree(ASGN, v, (*optree[op])(oper[op], v, e)); } static Tree expr2(void) { Tree p = expr3(4); if (t == '?') { Tree l, r; Coordinate pts[2]; if (Aflag > 1 && isfunc(p->type)) warning("%s used in a conditional expression\n", funcname(p)); p = pointer(p); t = gettok(); pts[0] = src; l = pointer(expr(':')); pts[1] = src; r = pointer(expr2()); if (events.points) { apply(events.points, &pts[0], &l); apply(events.points, &pts[1], &r); } p = condtree(p, l, r); } return p; } Tree value(Tree p) { int op = generic(rightkid(p)->op); if (p->type != voidtype && (op==AND || op==OR || op==NOT || op==EQ || op==NE || op== LE || op==LT || op== GE || op==GT)) p = condtree(p, consttree(1, inttype), consttree(0, inttype)); return p; } static Tree expr3(int k) { int k1; Tree p = unary(); for (k1 = prec[t]; k1 >= k; k1--) while (prec[t] == k1 && *cp != '=') { Tree r; Coordinate pt; int op = t; t = gettok(); pt = src; p = pointer(p); if (op == ANDAND || op == OROR) { r = pointer(expr3(k1)); if (events.points) apply(events.points, &pt, &r); } else r = pointer(expr3(k1 + 1)); p = (*optree[op])(oper[op], p, r); } return p; } static Tree unary(void) { Tree p; switch (t) { case '*': t = gettok(); p = unary(); p = pointer(p); if (isptr(p->type) && (isfunc(p->type->type) || isarray(p->type->type))) p = retype(p, p->type->type); else { if (YYnull) p = nullcheck(p); p = rvalue(p); } break; case '&': t = gettok(); p = unary(); if (isarray(p->type) || isfunc(p->type)) p = retype(p, ptr(p->type)); else p = lvalue(p); if (isaddrop(p->op) && p->u.sym->sclass == REGISTER) error("invalid operand of unary &; `%s' is declared register\n", p->u.sym->name); else if (isaddrop(p->op)) p->u.sym->addressed = 1; break; case '+': t = gettok(); p = unary(); p = pointer(p); if (isarith(p->type)) p = cast(p, promote(p->type)); else typeerror(ADD, p, NULL); break; case '-': t = gettok(); p = unary(); p = pointer(p); if (isarith(p->type)) { Type ty = promote(p->type); p = cast(p, ty); if (isunsigned(ty)) { warning("unsigned operand of unary -\n"); p = simplify(ADD, ty, simplify(BCOM, ty, p, NULL), cnsttree(ty, 1UL)); } else p = simplify(NEG, ty, p, NULL); } else typeerror(SUB, p, NULL); break; case '~': t = gettok(); p = unary(); p = pointer(p); if (isint(p->type)) { Type ty = promote(p->type); p = simplify(BCOM, ty, cast(p, ty), NULL); } else typeerror(BCOM, p, NULL); break; case '!': t = gettok(); p = unary(); p = pointer(p); if (isscalar(p->type)) p = simplify(NOT, inttype, cond(p), NULL); else typeerror(NOT, p, NULL); break; case INCR: t = gettok(); p = unary(); p = incr(INCR, pointer(p), consttree(1, inttype)); break; case DECR: t = gettok(); p = unary(); p = incr(DECR, pointer(p), consttree(1, inttype)); break; case TYPECODE: case SIZEOF: { int op = t; Type ty; p = NULL; t = gettok(); if (t == '(') { t = gettok(); if (istypename(t, tsym)) { ty = typename(); expect(')'); } else { p = postfix(expr(')')); ty = p->type; } } else { p = unary(); ty = p->type; } assert(ty); if (op == TYPECODE) p = cnsttree(inttype, (long)ty->op); else { if (isfunc(ty) || ty->size == 0) error("invalid type argument `%t' to `sizeof'\n", ty); else if (p && rightkid(p)->op == FIELD) error("`sizeof' applied to a bit field\n"); p = cnsttree(unsignedlong, (unsigned long)ty->size); } } break; case '(': t = gettok(); if (istypename(t, tsym)) { Type ty, ty1 = typename(), pty; expect(')'); ty = unqual(ty1); if (isenum(ty)) { Type ty2 = ty->type; if (isconst(ty1)) ty2 = qual(CONST, ty2); if (isvolatile(ty1)) ty2 = qual(VOLATILE, ty2); ty1 = ty2; ty = ty->type; } p = pointer(unary()); pty = p->type; if (isenum(pty)) pty = pty->type; if ((isarith(pty) && isarith(ty)) || (isptr(pty) && isptr(ty))) { explicitCast++; p = cast(p, ty); explicitCast--; } else if ((isptr(pty) && isint(ty)) || (isint(pty) && isptr(ty))) { if (Aflag >= 1 && ty->size < pty->size) warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, ty); p = cast(p, ty); } else if (ty != voidtype) { error("cast from `%t' to `%t' is illegal\n", p->type, ty1); ty1 = inttype; } if (generic(p->op) == INDIR || ty->size == 0) p = tree(RIGHT, ty1, NULL, p); else p = retype(p, ty1); } else p = postfix(expr(')')); break; default: p = postfix(primary()); } return p; } static Tree postfix(Tree p) { for (;;) switch (t) { case INCR: p = tree(RIGHT, p->type, tree(RIGHT, p->type, p, incr(t, p, consttree(1, inttype))), p); t = gettok(); break; case DECR: p = tree(RIGHT, p->type, tree(RIGHT, p->type, p, incr(t, p, consttree(1, inttype))), p); t = gettok(); break; case '[': { Tree q; t = gettok(); q = expr(']'); if (YYnull) { if (isptr(p->type)) p = nullcheck(p); else if (isptr(q->type)) q = nullcheck(q); } p = (*optree['+'])(ADD, pointer(p), pointer(q)); if (isptr(p->type) && isarray(p->type->type)) p = retype(p, p->type->type); else p = rvalue(p); } break; case '(': { Type ty; Coordinate pt; p = pointer(p); if (isptr(p->type) && isfunc(p->type->type)) ty = p->type->type; else { error("found `%t' expected a function\n", p->type); ty = func(voidtype, NULL, 1); p = retype(p, ptr(ty)); } pt = src; t = gettok(); p = call(p, ty, pt); } break; case '.': t = gettok(); if (t == ID) { if (isstruct(p->type)) { Tree q = addrof(p); p = field(q, token); q = rightkid(q); if (isaddrop(q->op) && q->u.sym->temporary) p = tree(RIGHT, p->type, p, NULL); } else error("left operand of . has incompatible type `%t'\n", p->type); t = gettok(); } else error("field name expected\n"); break; case DEREF: t = gettok(); p = pointer(p); if (t == ID) { if (isptr(p->type) && isstruct(p->type->type)) { if (YYnull) p = nullcheck(p); p = field(p, token); } else error("left operand of -> has incompatible type `%t'\n", p->type); t = gettok(); } else error("field name expected\n"); break; default: return p; } } static Tree primary(void) { Tree p; assert(t != '('); switch (t) { case ICON: case FCON: p = tree(mkop(CNST,tsym->type), tsym->type, NULL, NULL); p->u.v = tsym->u.c.v; break; case SCON: if (ischar(tsym->type->type)) tsym->u.c.v.p = stringn(tsym->u.c.v.p, tsym->type->size); else tsym->u.c.v.p = memcpy(allocate(tsym->type->size, PERM), tsym->u.c.v.p, tsym->type->size); tsym = constant(tsym->type, tsym->u.c.v); if (tsym->u.c.loc == NULL) tsym->u.c.loc = genident(STATIC, tsym->type, GLOBAL); p = idtree(tsym->u.c.loc); break; case ID: if (tsym == NULL) { Symbol p = install(token, &identifiers, level, FUNC); p->src = src; if (getchr() == '(') { Symbol q = lookup(token, externals); p->type = func(inttype, NULL, 1); p->sclass = EXTERN; if (Aflag >= 1) warning("missing prototype\n"); if (q && !eqtype(q->type, p->type, 1)) warning("implicit declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src); if (q == NULL) { q = install(p->name, &externals, GLOBAL, PERM); q->type = p->type; q->sclass = EXTERN; q->src = src; (*IR->defsymbol)(q); } p->u.alias = q; } else { error("undeclared identifier `%s'\n", p->name); p->sclass = AUTO; p->type = inttype; if (p->scope == GLOBAL) (*IR->defsymbol)(p); else addlocal(p); } t = gettok(); if (xref) use(p, src); return idtree(p); } if (xref) use(tsym, src); if (tsym->sclass == ENUM) p = consttree(tsym->u.value, inttype); else { if (tsym->sclass == TYPEDEF) error("illegal use of type name `%s'\n", tsym->name); p = idtree(tsym); } break; case FIRSTARG: if (level > PARAM && cfunc && cfunc->u.f.callee[0]) p = idtree(cfunc->u.f.callee[0]); else { error("illegal use of `%k'\n", FIRSTARG); p = cnsttree(inttype, 0L); } break; default: error("illegal expression\n"); p = cnsttree(inttype, 0L); } t = gettok(); return p; } Tree idtree(Symbol p) { int op; Tree e; Type ty = p->type ? unqual(p->type) : voidptype; if (p->scope == GLOBAL || p->sclass == STATIC) op = ADDRG; else if (p->scope == PARAM) { op = ADDRF; if (isstruct(p->type) && !IR->wants_argb) { e = tree(mkop(op,voidptype), ptr(ptr(p->type)), NULL, NULL); e->u.sym = p; return rvalue(rvalue(e)); } } else if (p->sclass == EXTERN) { assert(p->u.alias); p = p->u.alias; op = ADDRG; } else op = ADDRL; p->ref += refinc; if (isarray(ty)) e = tree(mkop(op,voidptype), p->type, NULL, NULL); else if (isfunc(ty)) e = tree(mkop(op,funcptype), p->type, NULL, NULL); else e = tree(mkop(op,voidptype), ptr(p->type), NULL, NULL); e->u.sym = p; if (isptr(e->type)) e = rvalue(e); return e; } Tree rvalue(Tree p) { Type ty = deref(p->type); ty = unqual(ty); return tree(mkop(INDIR,ty), ty, p, NULL); } Tree lvalue(Tree p) { if (generic(p->op) != INDIR) { error("lvalue required\n"); return value(p); } else if (unqual(p->type) == voidtype) warning("`%t' used as an lvalue\n", p->type); return p->kids[0]; } Tree retype(Tree p, Type ty) { Tree q; if (p->type == ty) return p; q = tree(p->op, ty, p->kids[0], p->kids[1]); q->node = p->node; q->u = p->u; return q; } Tree rightkid(Tree p) { while (p && p->op == RIGHT) if (p->kids[1]) p = p->kids[1]; else if (p->kids[0]) p = p->kids[0]; else assert(0); assert(p); return p; } int hascall(Tree p) { if (p == 0) return 0; if (generic(p->op) == CALL || (IR->mulops_calls && (p->op == DIV+I || p->op == MOD+I || p->op == MUL+I || p->op == DIV+U || p->op == MOD+U || p->op == MUL+U))) return 1; return hascall(p->kids[0]) || hascall(p->kids[1]); } Type binary(Type xty, Type yty) { #define xx(t) if (xty == t || yty == t) return t xx(longdouble); xx(doubletype); xx(floattype); xx(unsignedlonglong); xx(longlong); xx(unsignedlong); if ((xty == longtype && yty == unsignedtype) || (xty == unsignedtype && yty == longtype)) { if (longtype->size > unsignedtype->size) return longtype; else return unsignedlong; } xx(longtype); xx(unsignedtype); return inttype; #undef xx } Tree pointer(Tree p) { if (isarray(p->type)) /* assert(p->op != RIGHT || p->u.sym == NULL), */ p = retype(p, atop(p->type)); else if (isfunc(p->type)) p = retype(p, ptr(p->type)); return p; } Tree cond(Tree p) { int op = generic(rightkid(p)->op); if (op == AND || op == OR || op == NOT || op == EQ || op == NE || op == LE || op == LT || op == GE || op == GT) return p; p = pointer(p); return (*optree[NEQ])(NE, p, consttree(0, inttype)); } Tree cast(Tree p, Type type) { Type src, dst; p = value(p); if (p->type == type) return p; dst = unqual(type); src = unqual(p->type); if (src->op != dst->op || src->size != dst->size) { switch (src->op) { case INT: if (src->size < inttype->size) p = simplify(CVI, inttype, p, NULL); break; case UNSIGNED: if (src->size < inttype->size) p = simplify(CVU, inttype, p, NULL); else if (src->size < unsignedtype->size) p = simplify(CVU, unsignedtype, p, NULL); break; case ENUM: p = retype(p, inttype); break; case POINTER: if (isint(dst) && src->size > dst->size) warning("conversion from `%t' to `%t' is undefined\n", p->type, type); p = simplify(CVP, super(src), p, NULL); break; case FLOAT: break; default: assert(0); } { src = unqual(p->type); dst = super(dst); if (src->op != dst->op) switch (src->op) { case INT: p = simplify(CVI, dst, p, NULL); break; case UNSIGNED: if (isfloat(dst)) { Type ssrc = signedint(src); Tree two = cnsttree(longdouble, (double)2.0); p = (*optree['+'])(ADD, (*optree['*'])(MUL, two, simplify(CVU, ssrc, simplify(RSH, src, p, consttree(1, inttype)), NULL)), simplify(CVU, ssrc, simplify(BAND, src, p, consttree(1, unsignedtype)), NULL)); } else p = simplify(CVU, dst, p, NULL); break; case FLOAT: if (isunsigned(dst)) { Type sdst = signedint(dst); Tree c = cast(cnsttree(longdouble, (double)sdst->u.sym->u.limits.max.i + 1), src); p = condtree( simplify(GE, src, p, c), (*optree['+'])(ADD, cast(cast(simplify(SUB, src, p, c), sdst), dst), cast(cnsttree(unsignedlong, (unsigned long)sdst->u.sym->u.limits.max.i + 1), dst)), simplify(CVF, sdst, p, NULL)); } else p = simplify(CVF, dst, p, NULL); break; default: assert(0); } dst = unqual(type); } } src = unqual(p->type); switch (src->op) { case INT: if (src->op != dst->op || src->size != dst->size) p = simplify(CVI, dst, p, NULL); break; case UNSIGNED: if (src->op != dst->op || src->size != dst->size) p = simplify(CVU, dst, p, NULL); break; case FLOAT: if (src->op != dst->op || src->size != dst->size) p = simplify(CVF, dst, p, NULL); break; case POINTER: if (src->op != dst->op) p = simplify(CVP, dst, p, NULL); else { if ((isfunc(src->type) && !isfunc(dst->type)) || (!isfunc(src->type) && isfunc(dst->type))) warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, type); if (src->size != dst->size) p = simplify(CVP, dst, p, NULL); } break; default: assert(0); } return retype(p, type); } Tree field(Tree p, const char *name) { Field q; Type ty1, ty = p->type; if (isptr(ty)) ty = deref(ty); ty1 = ty; ty = unqual(ty); if ((q = fieldref(name, ty)) != NULL) { if (isarray(q->type)) { ty = q->type->type; if (isconst(ty1) && !isconst(ty)) ty = qual(CONST, ty); if (isvolatile(ty1) && !isvolatile(ty)) ty = qual(VOLATILE, ty); ty = array(ty, q->type->size/ty->size, q->type->align); } else { ty = q->type; if (isconst(ty1) && !isconst(ty)) ty = qual(CONST, ty); if (isvolatile(ty1) && !isvolatile(ty)) ty = qual(VOLATILE, ty); ty = ptr(ty); } if (YYcheck && !isaddrop(p->op) && q->offset > 0) /* omit */ p = nullcall(ty, YYcheck, p, consttree(q->offset, inttype)); /* omit */ else /* omit */ p = simplify(ADD+P, ty, p, consttree(q->offset, inttype)); if (q->lsb) { p = tree(FIELD, ty->type, rvalue(p), NULL); p->u.field = q; } else if (!isarray(q->type)) p = rvalue(p); } else { error("unknown field `%s' of `%t'\n", name, ty); p = rvalue(retype(p, ptr(inttype))); } return p; } /* funcname - return name of function f or a function' */ char *funcname(Tree f) { if (isaddrop(f->op)) return stringf("`%s'", f->u.sym->name); return "a function"; } static Tree nullcheck(Tree p) { if (!needconst && YYnull && isptr(p->type)) { p = value(p); if (strcmp(YYnull->name, "_YYnull") == 0) { Symbol t1 = temporary(REGISTER, voidptype); p = tree(RIGHT, p->type, tree(OR, voidtype, cond(asgn(t1, cast(p, voidptype))), vcall(YYnull, voidtype, (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL)), idtree(t1)); } else p = nullcall(p->type, YYnull, p, cnsttree(inttype, 0L)); } return p; } Tree nullcall(Type pty, Symbol f, Tree p, Tree e) { Type ty; if (isarray(pty)) return retype(nullcall(atop(pty), f, p, e), pty); ty = unqual(unqual(p->type)->type); return vcall(f, pty, p, e, cnsttree(inttype, (long)ty->size), cnsttree(inttype, (long)ty->align), (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL); } openarena_0.8.8.orig/code/tools/lcc/src/list.c0000644000175000017500000000171711656310263017754 0ustar smcvsmcv#include "c.h" static List freenodes; /* free list nodes */ /* append - append x to list, return new list */ List append(void *x, List list) { List new; if ((new = freenodes) != NULL) freenodes = freenodes->link; else NEW(new, PERM); if (list) { new->link = list->link; list->link = new; } else new->link = new; new->x = x; return new; } /* length - # elements in list */ int length(List list) { int n = 0; if (list) { List lp = list; do n++; while ((lp = lp->link) != list); } return n; } /* ltov - convert list to an NULL-terminated vector allocated in arena */ void *ltov(List *list, unsigned arena) { int i = 0; void **array = newarray(length(*list) + 1, sizeof array[0], arena); if (*list) { List lp = *list; do { lp = lp->link; array[i++] = lp->x; } while (lp != *list); #ifndef PURIFY lp = (*list)->link; (*list)->link = freenodes; freenodes = lp; #endif } *list = NULL; array[i] = NULL; return array; } openarena_0.8.8.orig/code/tools/lcc/src/dagcheck.md0000644000175000017500000000751211656310262020706 0ustar smcvsmcv%{ #include "c.h" typedef Node NODEPTR_TYPE; #define OP_LABEL(p) (specific((p)->op)) #define LEFT_CHILD(p) ((p)->kids[0]) #define RIGHT_CHILD(p) ((p)->kids[1]) #define STATE_LABEL(p) ((p)->x.state) #define PANIC error %} %term CNSTF=17 CNSTI=21 CNSTP=23 CNSTU=22 %term ARGB=41 ARGF=33 ARGI=37 ARGP=39 ARGU=38 %term ASGNB=57 ASGNF=49 ASGNI=53 ASGNP=55 ASGNU=54 %term INDIRB=73 INDIRF=65 INDIRI=69 INDIRP=71 INDIRU=70 %term CVFF=113 CVFI=117 %term CVIF=129 CVII=133 CVIU=134 %term CVPP=151 CVPU=150 %term CVUI=181 CVUP=183 CVUU=182 %term NEGF=193 NEGI=197 %term CALLB=217 CALLF=209 CALLI=213 CALLP=215 CALLU=214 CALLV=216 %term RETF=241 RETI=245 RETP=247 RETU=246 RETV=248 %term ADDRGP=263 %term ADDRFP=279 %term ADDRLP=295 %term ADDF=305 ADDI=309 ADDP=311 ADDU=310 %term SUBF=321 SUBI=325 SUBP=327 SUBU=326 %term LSHI=341 LSHU=342 %term MODI=357 MODU=358 %term RSHI=373 RSHU=374 %term BANDI=389 BANDU=390 %term BCOMI=405 BCOMU=406 %term BORI=421 BORU=422 %term BXORI=437 BXORU=438 %term DIVF=449 DIVI=453 DIVU=454 %term MULF=465 MULI=469 MULU=470 %term EQF=481 EQI=485 EQU=486 %term GEF=497 GEI=501 GEU=502 %term GTF=513 GTI=517 GTU=518 %term LEF=529 LEI=533 LEU=534 %term LTF=545 LTI=549 LTU=550 %term NEF=561 NEI=565 NEU=566 %term JUMPV=584 %term LABELV=600 %% stmt: INDIRB(P) "" stmt: INDIRF(P) "" stmt: INDIRI(P) "" stmt: INDIRU(P) "" stmt: INDIRP(P) "" stmt: CALLF(P) "" stmt: CALLI(P) "" stmt: CALLU(P) "" stmt: CALLP(P) "" stmt: V "" bogus: I "" 1 bogus: U "" 1 bogus: P "" 1 bogus: F "" 1 bogus: B "" 1 bogus: V "" 1 I: bogus "" 1 U: bogus "" 1 P: bogus "" 1 F: bogus "" 1 B: bogus "" 1 V: bogus "" 1 F: CNSTF "" I: CNSTI "" P: CNSTP "" U: CNSTU "" V: ARGB(B) "" V: ARGF(F) "" V: ARGI(I) "" V: ARGU(U) "" V: ARGP(P) "" V: ASGNB(P,B) "" V: ASGNF(P,F) "" V: ASGNI(P,I) "" V: ASGNU(P,U) "" V: ASGNP(P,P) "" B: INDIRB(P) "" F: INDIRF(P) "" I: INDIRI(P) "" U: INDIRU(P) "" P: INDIRP(P) "" I: CVII(I) "" I: CVUI(U) "" I: CVFI(F) "" U: CVIU(I) "" U: CVUU(U) "" U: CVPU(P) "" F: CVIF(I) "" F: CVFF(F) "" P: CVUP(U) "" P: CVPP(P) "" F: NEGF(F) "" I: NEGI(I) "" V: CALLB(P,P) "" F: CALLF(P) "" I: CALLI(P) "" U: CALLU(P) "" P: CALLP(P) "" V: CALLV(P) "" V: RETF(F) "" V: RETI(I) "" V: RETU(U) "" V: RETP(P) "" V: RETV "" P: ADDRGP "" P: ADDRFP "" P: ADDRLP "" F: ADDF(F,F) "" I: ADDI(I,I) "" P: ADDP(P,I) "" P: ADDP(I,P) "" P: ADDP(U,P) "" P: ADDP(P,U) "" U: ADDU(U,U) "" F: SUBF(F,F) "" I: SUBI(I,I) "" P: SUBP(P,I) "" P: SUBP(P,U) "" U: SUBU(U,U) "" I: LSHI(I,I) "" U: LSHU(U,I) "" I: MODI(I,I) "" U: MODU(U,U) "" I: RSHI(I,I) "" U: RSHU(U,I) "" U: BANDU(U,U) "" I: BANDI(I,I) "" U: BCOMU(U) "" I: BCOMI(I) "" I: BORI(I,I) "" U: BORU(U,U) "" U: BXORU(U,U) "" I: BXORI(I,I) "" F: DIVF(F,F) "" I: DIVI(I,I) "" U: DIVU(U,U) "" F: MULF(F,F) "" I: MULI(I,I) "" U: MULU(U,U) "" V: EQF(F,F) "" V: EQI(I,I) "" V: EQU(U,U) "" V: GEF(F,F) "" V: GEI(I,I) "" V: GEU(U,U) "" V: GTF(F,F) "" V: GTI(I,I) "" V: GTU(U,U) "" V: LEF(F,F) "" V: LEI(I,I) "" V: LEU(U,U) "" V: LTF(F,F) "" V: LTI(I,I) "" V: LTU(U,U) "" V: NEF(F,F) "" V: NEI(I,I) "" V: NEU(U,U) "" V: JUMPV(P) "" V: LABELV "" %% static void reduce(NODEPTR_TYPE p, int goalnt) { int i, sz = opsize(p->op), rulenumber = _rule(p->x.state, goalnt); short *nts = _nts[rulenumber]; NODEPTR_TYPE kids[10]; assert(rulenumber); _kids(p, rulenumber, kids); for (i = 0; nts[i]; i++) reduce(kids[i], nts[i]); switch (optype(p->op)) { #define xx(ty) if (sz == ty->size) return case I: case U: xx(chartype); xx(shorttype); xx(inttype); xx(longtype); xx(longlong); break; case F: xx(floattype); xx(doubletype); xx(longdouble); break; case P: xx(voidptype); xx(funcptype); break; case V: case B: if (sz == 0) return; #undef xx } printdag(p, 2); assert(0); } void check(Node p) { struct _state { short cost[1]; }; _label(p); if (((struct _state *)p->x.state)->cost[1] > 0) { printdag(p, 2); assert(0); } reduce(p, 1); } openarena_0.8.8.orig/code/tools/lcc/src/simp.c0000644000175000017500000004006111656310262017743 0ustar smcvsmcv#include "c.h" #include #define foldcnst(TYPE,VAR,OP) \ if (l->op == CNST+TYPE && r->op == CNST+TYPE) \ return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR) #define commute(L,R) \ if (generic(R->op) == CNST && generic(L->op) != CNST) \ do { Tree t = L; L = R; R = t; } while(0) #define xfoldcnst(TYPE,VAR,OP,FUNC)\ if (l->op == CNST+TYPE && r->op == CNST+TYPE\ && FUNC(l->u.v.VAR,r->u.v.VAR,\ ty->u.sym->u.limits.min.VAR,\ ty->u.sym->u.limits.max.VAR, needconst)) \ return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR) #define xcvtcnst(FTYPE,SRC,DST,VAR,EXPR) \ if (l->op == CNST+FTYPE) do {\ if (!explicitCast\ && ((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\ warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, DST);\ if (needconst\ || !((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\ return cnsttree(ty, (EXPR)); } while(0) #define identity(X,Y,TYPE,VAR,VAL) \ if (X->op == CNST+TYPE && X->u.v.VAR == VAL) return Y #define zerofield(OP,TYPE,VAR) \ if (l->op == FIELD \ && r->op == CNST+TYPE && r->u.v.VAR == 0)\ return eqtree(OP, bittree(BAND, l->kids[0],\ cnsttree(unsignedtype, \ (unsigned long)fieldmask(l->u.field)<u.field))), r) #define cfoldcnst(TYPE,VAR,OP) \ if (l->op == CNST+TYPE && r->op == CNST+TYPE) \ return cnsttree(inttype, (long)(l->u.v.VAR OP r->u.v.VAR)) #define foldaddp(L,R,RTYPE,VAR) \ if (L->op == CNST+P && R->op == CNST+RTYPE) { \ Tree e = tree(CNST+P, ty, NULL, NULL);\ e->u.v.p = (char *)L->u.v.p + R->u.v.VAR;\ return e; } #define ufoldcnst(TYPE,EXP) if (l->op == CNST+TYPE) return EXP #define sfoldcnst(OP) \ if (l->op == CNST+U && r->op == CNST+I \ && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) \ return cnsttree(ty, (unsigned long)(l->u.v.u OP r->u.v.i)) #define geu(L,R,V) \ if (R->op == CNST+U && R->u.v.u == 0) do { \ warning("result of unsigned comparison is constant\n"); \ return tree(RIGHT, inttype, root(L), cnsttree(inttype, (long)(V))); } while(0) #define idempotent(OP) if (l->op == OP) return l->kids[0] int needconst; int explicitCast; static int addi(long x, long y, long min, long max, int needconst) { int cond = x == 0 || y == 0 || (x < 0 && y < 0 && x >= min - y) || (x < 0 && y > 0) || (x > 0 && y < 0) || (x > 0 && y > 0 && x <= max - y); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } static int addd(double x, double y, double min, double max, int needconst) { int cond = x == 0 || y == 0 || (x < 0 && y < 0 && x >= min - y) || (x < 0 && y > 0) || (x > 0 && y < 0) || (x > 0 && y > 0 && x <= max - y); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } static Tree addrtree(Tree e, long n, Type ty) { Symbol p = e->u.sym, q; if (p->scope == GLOBAL || p->sclass == STATIC || p->sclass == EXTERN) NEW0(q, PERM); else NEW0(q, FUNC); q->name = stringd(genlabel(1)); q->sclass = p->sclass; q->scope = p->scope; assert(isptr(ty) || isarray(ty)); q->type = isptr(ty) ? ty->type : ty; q->temporary = p->temporary; q->generated = p->generated; q->addressed = p->addressed; q->computed = 1; q->defined = 1; q->ref = 1; if (p->scope == GLOBAL || p->sclass == STATIC || p->sclass == EXTERN) { if (p->sclass == AUTO) q->sclass = STATIC; (*IR->address)(q, p, n); } else { Code cp; addlocal(p); cp = code(Address); cp->u.addr.sym = q; cp->u.addr.base = p; cp->u.addr.offset = n; } e = tree(e->op, ty, NULL, NULL); e->u.sym = q; return e; } /* div[id] - return 1 if min <= x/y <= max, 0 otherwise */ static int divi(long x, long y, long min, long max, int needconst) { int cond = y != 0 && !(x == min && y == -1); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } static int divd(double x, double y, double min, double max, int needconst) { int cond; if (x < 0) x = -x; if (y < 0) y = -y; cond = y != 0 && !(y < 1 && x > max*y); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } /* mul[id] - return 1 if min <= x*y <= max, 0 otherwise */ static int muli(long x, long y, long min, long max, int needconst) { int cond = (x > -1 && x <= 1) || (y > -1 && y <= 1) || (x < 0 && y < 0 && -x <= max/-y) || (x < 0 && y > 0 && x >= min/y) || (x > 0 && y < 0 && y >= min/x) || (x > 0 && y > 0 && x <= max/y); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } static int muld(double x, double y, double min, double max, int needconst) { int cond = (x >= -1 && x <= 1) || (y >= -1 && y <= 1) || (x < 0 && y < 0 && -x <= max/-y) || (x < 0 && y > 0 && x >= min/y) || (x > 0 && y < 0 && y >= min/x) || (x > 0 && y > 0 && x <= max/y); if (!cond && needconst) { warning("overflow in constant expression\n"); cond = 1; } return cond; } /* sub[id] - return 1 if min <= x-y <= max, 0 otherwise */ static int subi(long x, long y, long min, long max, int needconst) { return addi(x, -y, min, max, needconst); } static int subd(double x, double y, double min, double max, int needconst) { return addd(x, -y, min, max, needconst); } Tree constexpr(int tok) { Tree p; needconst++; p = expr1(tok); needconst--; return p; } int intexpr(int tok, int n) { Tree p = constexpr(tok); needconst++; if (p->op == CNST+I || p->op == CNST+U) n = cast(p, inttype)->u.v.i; else error("integer expression must be constant\n"); needconst--; return n; } Tree simplify(int op, Type ty, Tree l, Tree r) { int n; if (optype(op) == 0) op = mkop(op, ty); switch (op) { case ADD+U: foldcnst(U,u,+); commute(r,l); identity(r,l,U,u,0); break; case ADD+I: xfoldcnst(I,i,+,addi); commute(r,l); identity(r,l,I,i,0); break; case CVI+I: xcvtcnst(I,l->u.v.i,ty,i,(long)extend(l->u.v.i,ty)); break; case CVU+I: if (l->op == CNST+U) { if (!explicitCast && l->u.v.u > ty->u.sym->u.limits.max.i) warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, ty); if (needconst || !(l->u.v.u > ty->u.sym->u.limits.max.i)) return cnsttree(ty, (long)extend(l->u.v.u,ty)); } break; case CVP+U: xcvtcnst(P,(unsigned long)l->u.v.p,ty,u,(unsigned long)l->u.v.p); break; case CVU+P: xcvtcnst(U,(void*)l->u.v.u,ty,p,(void*)l->u.v.u); break; case CVP+P: xcvtcnst(P,l->u.v.p,ty,p,l->u.v.p); break; case CVI+U: xcvtcnst(I,l->u.v.i,ty,u,((unsigned long)l->u.v.i)&ones(8*ty->size)); break; case CVU+U: xcvtcnst(U,l->u.v.u,ty,u,l->u.v.u&ones(8*ty->size)); break; case CVI+F: xcvtcnst(I,l->u.v.i,ty,d,(double)l->u.v.i); case CVU+F: xcvtcnst(U,l->u.v.u,ty,d,(double)l->u.v.u); break; case CVF+I: xcvtcnst(F,l->u.v.d,ty,i,(long)l->u.v.d); break; case CVF+F: { float d = 0.0f; if (l->op == CNST+F) { if (l->u.v.d < ty->u.sym->u.limits.min.d) d = ty->u.sym->u.limits.min.d; else if (l->u.v.d > ty->u.sym->u.limits.max.d) d = ty->u.sym->u.limits.max.d; else d = l->u.v.d; } xcvtcnst(F,l->u.v.d,ty,d,(double)d); break; } case BAND+U: foldcnst(U,u,&); commute(r,l); identity(r,l,U,u,ones(8*ty->size)); if (r->op == CNST+U && r->u.v.u == 0) return tree(RIGHT, ty, root(l), cnsttree(ty, 0UL)); break; case BAND+I: foldcnst(I,i,&); commute(r,l); identity(r,l,I,i,ones(8*ty->size)); if (r->op == CNST+I && r->u.v.u == 0) return tree(RIGHT, ty, root(l), cnsttree(ty, 0L)); break; case MUL+U: commute(l,r); if (l->op == CNST+U && (n = ispow2(l->u.v.u)) != 0) return simplify(LSH, ty, r, cnsttree(inttype, (long)n)); foldcnst(U,u,*); identity(r,l,U,u,1); break; case NE+I: cfoldcnst(I,i,!=); commute(r,l); zerofield(NE,I,i); break; case EQ+I: cfoldcnst(I,i,==); commute(r,l); zerofield(EQ,I,i); break; case ADD+P: foldaddp(l,r,I,i); foldaddp(l,r,U,u); foldaddp(r,l,I,i); foldaddp(r,l,U,u); commute(r,l); identity(r,retype(l,ty),I,i,0); identity(r,retype(l,ty),U,u,0); if (isaddrop(l->op) && ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i && r->u.v.i >= longtype->u.sym->u.limits.min.i) || (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i))) return addrtree(l, cast(r, longtype)->u.v.i, ty); if (l->op == ADD+P && isaddrop(l->kids[1]->op) && ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i && r->u.v.i >= longtype->u.sym->u.limits.min.i) || (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i))) return simplify(ADD+P, ty, l->kids[0], addrtree(l->kids[1], cast(r, longtype)->u.v.i, ty)); if ((l->op == ADD+I || l->op == SUB+I) && l->kids[1]->op == CNST+I && isaddrop(r->op)) return simplify(ADD+P, ty, l->kids[0], simplify(generic(l->op)+P, ty, r, l->kids[1])); if (l->op == ADD+P && generic(l->kids[1]->op) == CNST && generic(r->op) == CNST) return simplify(ADD+P, ty, l->kids[0], simplify(ADD, l->kids[1]->type, l->kids[1], r)); if (l->op == ADD+I && generic(l->kids[1]->op) == CNST && r->op == ADD+P && generic(r->kids[1]->op) == CNST) return simplify(ADD+P, ty, l->kids[0], simplify(ADD+P, ty, r->kids[0], simplify(ADD, r->kids[1]->type, l->kids[1], r->kids[1]))); if (l->op == RIGHT && l->kids[1]) return tree(RIGHT, ty, l->kids[0], simplify(ADD+P, ty, l->kids[1], r)); else if (l->op == RIGHT && l->kids[0]) return tree(RIGHT, ty, simplify(ADD+P, ty, l->kids[0], r), NULL); break; case ADD+F: xfoldcnst(F,d,+,addd); commute(r,l); break; case AND+I: op = AND; ufoldcnst(I,l->u.v.i ? cond(r) : l); /* 0&&r => 0, 1&&r => r */ break; case OR+I: op = OR; /* 0||r => r, 1||r => 1 */ ufoldcnst(I,l->u.v.i ? cnsttree(ty, 1L) : cond(r)); break; case BCOM+I: ufoldcnst(I,cnsttree(ty, (long)extend((~l->u.v.i)&ones(8*ty->size), ty))); idempotent(BCOM+U); break; case BCOM+U: ufoldcnst(U,cnsttree(ty, (unsigned long)((~l->u.v.u)&ones(8*ty->size)))); idempotent(BCOM+U); break; case BOR+U: foldcnst(U,u,|); commute(r,l); identity(r,l,U,u,0); break; case BOR+I: foldcnst(I,i,|); commute(r,l); identity(r,l,I,i,0); break; case BXOR+U: foldcnst(U,u,^); commute(r,l); identity(r,l,U,u,0); break; case BXOR+I: foldcnst(I,i,^); commute(r,l); identity(r,l,I,i,0); break; case DIV+F: xfoldcnst(F,d,/,divd); break; case DIV+I: identity(r,l,I,i,1); if ((r->op == CNST+I && r->u.v.i == 0) || (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i && r->op == CNST+I && r->u.v.i == -1)) break; xfoldcnst(I,i,/,divi); break; case DIV+U: identity(r,l,U,u,1); if (r->op == CNST+U && r->u.v.u == 0) break; if (r->op == CNST+U && (n = ispow2(r->u.v.u)) != 0) return simplify(RSH, ty, l, cnsttree(inttype, (long)n)); foldcnst(U,u,/); break; case EQ+F: cfoldcnst(F,d,==); commute(r,l); break; case EQ+U: cfoldcnst(U,u,==); commute(r,l); zerofield(EQ,U,u); break; case GE+F: cfoldcnst(F,d,>=); break; case GE+I: cfoldcnst(I,i,>=); break; case GE+U: geu(l,r,1); /* l >= 0 => (l,1) */ cfoldcnst(U,u,>=); if (l->op == CNST+U && l->u.v.u == 0) /* 0 >= r => r == 0 */ return eqtree(EQ, r, l); break; case GT+F: cfoldcnst(F,d, >); break; case GT+I: cfoldcnst(I,i, >); break; case GT+U: geu(r,l,0); /* 0 > r => (r,0) */ cfoldcnst(U,u, >); if (r->op == CNST+U && r->u.v.u == 0) /* l > 0 => l != 0 */ return eqtree(NE, l, r); break; case LE+F: cfoldcnst(F,d,<=); break; case LE+I: cfoldcnst(I,i,<=); break; case LE+U: geu(r,l,1); /* 0 <= r => (r,1) */ cfoldcnst(U,u,<=); if (r->op == CNST+U && r->u.v.u == 0) /* l <= 0 => l == 0 */ return eqtree(EQ, l, r); break; case LSH+I: identity(r,l,I,i,0); if (l->op == CNST+I && r->op == CNST+I && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size && muli(l->u.v.i, 1<u.v.i, ty->u.sym->u.limits.min.i, ty->u.sym->u.limits.max.i, needconst)) return cnsttree(ty, (long)(l->u.v.i<u.v.i)); if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); break; } break; case LSH+U: identity(r,l,I,i,0); sfoldcnst(<<); if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); break; } break; case LT+F: cfoldcnst(F,d, <); break; case LT+I: cfoldcnst(I,i, <); break; case LT+U: geu(l,r,0); /* l < 0 => (l,0) */ cfoldcnst(U,u, <); if (l->op == CNST+U && l->u.v.u == 0) /* 0 < r => r != 0 */ return eqtree(NE, r, l); break; case MOD+I: if (r->op == CNST+I && r->u.v.i == 1) /* l%1 => (l,0) */ return tree(RIGHT, ty, root(l), cnsttree(ty, 0L)); if ((r->op == CNST+I && r->u.v.i == 0) || (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i && r->op == CNST+I && r->u.v.i == -1)) break; xfoldcnst(I,i,%,divi); break; case MOD+U: if (r->op == CNST+U && ispow2(r->u.v.u)) /* l%2^n => l&(2^n-1) */ return bittree(BAND, l, cnsttree(ty, r->u.v.u - 1)); if (r->op == CNST+U && r->u.v.u == 0) break; foldcnst(U,u,%); break; case MUL+F: xfoldcnst(F,d,*,muld); commute(l,r); break; case MUL+I: commute(l,r); xfoldcnst(I,i,*,muli); if (l->op == CNST+I && r->op == ADD+I && r->kids[1]->op == CNST+I) /* c1*(x + c2) => c1*x + c1*c2 */ return simplify(ADD, ty, simplify(MUL, ty, l, r->kids[0]), simplify(MUL, ty, l, r->kids[1])); if (l->op == CNST+I && r->op == SUB+I && r->kids[1]->op == CNST+I) /* c1*(x - c2) => c1*x - c1*c2 */ return simplify(SUB, ty, simplify(MUL, ty, l, r->kids[0]), simplify(MUL, ty, l, r->kids[1])); if (l->op == CNST+I && l->u.v.i > 0 && (n = ispow2(l->u.v.i)) != 0) /* 2^n * r => r<u.v.d)); idempotent(NEG+F); break; case NEG+I: if (l->op == CNST+I) { if (needconst && l->u.v.i == ty->u.sym->u.limits.min.i) warning("overflow in constant expression\n"); if (needconst || l->u.v.i != ty->u.sym->u.limits.min.i) return cnsttree(ty, -l->u.v.i); } idempotent(NEG+I); break; case NOT+I: op = NOT; ufoldcnst(I,cnsttree(ty, !l->u.v.i)); break; case RSH+I: identity(r,l,I,i,0); if (l->op == CNST+I && r->op == CNST+I && r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) { long n = l->u.v.i>>r->u.v.i; if (l->u.v.i < 0) n |= ~0UL<<(8*l->type->size - r->u.v.i); return cnsttree(ty, n); } if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); break; } break; case RSH+U: identity(r,l,I,i,0); sfoldcnst(>>); if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) { warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i); break; } break; case SUB+F: xfoldcnst(F,d,-,subd); break; case SUB+I: xfoldcnst(I,i,-,subi); identity(r,l,I,i,0); break; case SUB+U: foldcnst(U,u,-); identity(r,l,U,u,0); break; case SUB+P: if (l->op == CNST+P && r->op == CNST+P) return cnsttree(ty, (long)((char *)l->u.v.p - (char *)r->u.v.p)); if (r->op == CNST+I || r->op == CNST+U) return simplify(ADD, ty, l, cnsttree(inttype, r->op == CNST+I ? -r->u.v.i : -(long)r->u.v.u)); if (isaddrop(l->op) && r->op == ADD+I && r->kids[1]->op == CNST+I) /* l - (x + c) => l-c - x */ return simplify(SUB, ty, simplify(SUB, ty, l, r->kids[1]), r->kids[0]); break; default:assert(0); } return tree(op, ty, l, r); } /* ispow2 - if u > 1 && u == 2^n, return n, otherwise return 0 */ int ispow2(unsigned long u) { int n; if (u > 1 && (u&(u-1)) == 0) for (n = 0; u; u >>= 1, n++) if (u&1) return n; return 0; } openarena_0.8.8.orig/code/tools/lcc/src/gen.c0000644000175000017500000005164011656310263017552 0ustar smcvsmcv#include "c.h" #define readsreg(p) \ (generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P) #define setsrc(d) ((d) && (d)->x.regnode && \ (d)->x.regnode->set == src->x.regnode->set && \ (d)->x.regnode->mask&src->x.regnode->mask) #define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b)) static Symbol askfixedreg(Symbol); static Symbol askreg(Symbol, unsigned*); static void blkunroll(int, int, int, int, int, int, int[]); static void docall(Node); static void dumpcover(Node, int, int); static void dumpregs(char *, char *, char *); static void dumprule(int); static void dumptree(Node); static unsigned emitasm(Node, int); static void genreload(Node, Symbol, int); static void genspill(Symbol, Node, Symbol); static Symbol getreg(Symbol, unsigned*, Node); static int getrule(Node, int); static void linearize(Node, Node); static int moveself(Node); static void prelabel(Node); static Node* prune(Node, Node*); static void putreg(Symbol); static void ralloc(Node); static void reduce(Node, int); static int reprune(Node*, int, int, Node); static int requate(Node); static Node reuse(Node, int); static void rewrite(Node); static Symbol spillee(Symbol, unsigned mask[], Node); static void spillr(Symbol, Node); static int uses(Node, Regnode); int offset; int maxoffset; int framesize; int argoffset; int maxargoffset; int dalign, salign; int bflag = 0; /* omit */ int dflag = 0; int swap; unsigned (*emitter)(Node, int) = emitasm; static char NeedsReg[] = { 0, /* unused */ 1, /* CNST */ 0, 0, /* ARG ASGN */ 1, /* INDIR */ 0, 0, 1, 1, /* - - CVF CVI */ 1, 0, 1, 1, /* CVP - CVU NEG */ 1, /* CALL */ 1, /* LOAD */ 0, /* RET */ 1, 1, 1, /* ADDRG ADDRF ADDRL */ 1, 1, 1, 1, 1, /* ADD SUB LSH MOD RSH */ 1, 1, 1, 1, /* BAND BCOM BOR BXOR */ 1, 1, /* DIV MUL */ 0, 0, 0, 0, 0, 0, /* EQ GE GT LE LT NE */ 0, 0 /* JUMP LABEL */ }; Node head; unsigned freemask[2]; unsigned usedmask[2]; unsigned tmask[2]; unsigned vmask[2]; Symbol mkreg(char *fmt, int n, int mask, int set) { Symbol p; NEW0(p, PERM); p->name = p->x.name = stringf(fmt, n); NEW0(p->x.regnode, PERM); p->x.regnode->number = n; p->x.regnode->mask = mask<x.regnode->set = set; return p; } Symbol mkwildcard(Symbol *syms) { Symbol p; NEW0(p, PERM); p->name = p->x.name = "wildcard"; p->x.wildcard = syms; return p; } void mkauto(Symbol p) { assert(p->sclass == AUTO); offset = roundup(offset + p->type->size, p->type->align); p->x.offset = -offset; p->x.name = stringd(-offset); } void blockbeg(Env *e) { e->offset = offset; e->freemask[IREG] = freemask[IREG]; e->freemask[FREG] = freemask[FREG]; } void blockend(Env *e) { if (offset > maxoffset) maxoffset = offset; offset = e->offset; freemask[IREG] = e->freemask[IREG]; freemask[FREG] = e->freemask[FREG]; } int mkactual(int align, int size) { int n = roundup(argoffset, align); argoffset = n + size; return n; } static void docall(Node p) { p->syms[1] = p->syms[0]; p->syms[0] = intconst(argoffset); if (argoffset > maxargoffset) maxargoffset = argoffset; argoffset = 0; } void blkcopy(int dreg, int doff, int sreg, int soff, int size, int tmp[]) { assert(size >= 0); if (size == 0) return; else if (size <= 2) blkunroll(size, dreg, doff, sreg, soff, size, tmp); else if (size == 3) { blkunroll(2, dreg, doff, sreg, soff, 2, tmp); blkunroll(1, dreg, doff+2, sreg, soff+2, 1, tmp); } else if (size <= 16) { blkunroll(4, dreg, doff, sreg, soff, size&~3, tmp); blkcopy(dreg, doff+(size&~3), sreg, soff+(size&~3), size&3, tmp); } else (*IR->x.blkloop)(dreg, doff, sreg, soff, size, tmp); } static void blkunroll(int k, int dreg, int doff, int sreg, int soff, int size, int tmp[]) { int i; assert(IR->x.max_unaligned_load); if (k > IR->x.max_unaligned_load && (k > salign || k > dalign)) k = IR->x.max_unaligned_load; for (i = 0; i+k < size; i += 2*k) { (*IR->x.blkfetch)(k, soff+i, sreg, tmp[0]); (*IR->x.blkfetch)(k, soff+i+k, sreg, tmp[1]); (*IR->x.blkstore)(k, doff+i, dreg, tmp[0]); (*IR->x.blkstore)(k, doff+i+k, dreg, tmp[1]); } if (i < size) { (*IR->x.blkfetch)(k, i+soff, sreg, tmp[0]); (*IR->x.blkstore)(k, i+doff, dreg, tmp[0]); } } void parseflags(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) if (strcmp(argv[i], "-d") == 0) dflag = 1; else if (strcmp(argv[i], "-b") == 0) /* omit */ bflag = 1; /* omit */ } static int getrule(Node p, int nt) { int rulenum; assert(p); rulenum = (*IR->x._rule)(p->x.state, nt); if (!rulenum) { fprint(stderr, "(%x->op=%s at %w is corrupt.)\n", p, opname(p->op), &src); assert(0); } return rulenum; } static void reduce(Node p, int nt) { int rulenum, i; short *nts; Node kids[10]; p = reuse(p, nt); rulenum = getrule(p, nt); nts = IR->x._nts[rulenum]; (*IR->x._kids)(p, rulenum, kids); for (i = 0; nts[i]; i++) reduce(kids[i], nts[i]); if (IR->x._isinstruction[rulenum]) { assert(p->x.inst == 0 || p->x.inst == nt); p->x.inst = nt; if (p->syms[RX] && p->syms[RX]->temporary) { debug(fprint(stderr, "(using %s)\n", p->syms[RX]->name)); p->syms[RX]->x.usecount++; } } } static Node reuse(Node p, int nt) { struct _state { short cost[1]; }; Symbol r = p->syms[RX]; if (generic(p->op) == INDIR && p->kids[0]->op == VREG+P && r->u.t.cse && p->x.mayrecalc && ((struct _state*)r->u.t.cse->x.state)->cost[nt] == 0) return r->u.t.cse; else return p; } int mayrecalc(Node p) { int op; assert(p && p->syms[RX]); if (p->syms[RX]->u.t.cse == NULL) return 0; op = generic(p->syms[RX]->u.t.cse->op); if (op == CNST || op == ADDRF || op == ADDRG || op == ADDRL) { p->x.mayrecalc = 1; return 1; } else return 0; } static Node *prune(Node p, Node pp[]) { if (p == NULL) return pp; p->x.kids[0] = p->x.kids[1] = p->x.kids[2] = NULL; if (p->x.inst == 0) return prune(p->kids[1], prune(p->kids[0], pp)); else if (p->syms[RX] && p->syms[RX]->temporary && p->syms[RX]->x.usecount < 2) { p->x.inst = 0; debug(fprint(stderr, "(clobbering %s)\n", p->syms[RX]->name)); return prune(p->kids[1], prune(p->kids[0], pp)); } else { prune(p->kids[1], prune(p->kids[0], &p->x.kids[0])); *pp = p; return pp + 1; } } #define ck(i) return (i) ? 0 : LBURG_MAX int range(Node p, int lo, int hi) { Symbol s = p->syms[0]; switch (specific(p->op)) { case ADDRF+P: case ADDRL+P: ck(s->x.offset >= lo && s->x.offset <= hi); case CNST+I: ck(s->u.c.v.i >= lo && s->u.c.v.i <= hi); case CNST+U: ck(s->u.c.v.u >= lo && s->u.c.v.u <= hi); case CNST+P: ck(s->u.c.v.p == 0 && lo <= 0 && hi >= 0); } return LBURG_MAX; } static void dumptree(Node p) { if (p->op == VREG+P && p->syms[0]) { fprint(stderr, "VREGP(%s)", p->syms[0]->name); return; } else if (generic(p->op) == LOAD) { fprint(stderr, "LOAD("); dumptree(p->kids[0]); fprint(stderr, ")"); return; } fprint(stderr, "%s(", opname(p->op)); switch (generic(p->op)) { case CNST: case LABEL: case ADDRG: case ADDRF: case ADDRL: if (p->syms[0]) fprint(stderr, "%s", p->syms[0]->name); break; case RET: if (p->kids[0]) dumptree(p->kids[0]); break; case CVF: case CVI: case CVP: case CVU: case JUMP: case ARG: case BCOM: case NEG: case INDIR: dumptree(p->kids[0]); break; case CALL: if (optype(p->op) != B) { dumptree(p->kids[0]); break; } /* else fall thru */ case EQ: case NE: case GT: case GE: case LE: case LT: case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH: case ADD: case SUB: case DIV: case MUL: case MOD: dumptree(p->kids[0]); fprint(stderr, ", "); dumptree(p->kids[1]); break; default: assert(0); } fprint(stderr, ")"); } static void dumpcover(Node p, int nt, int in) { int rulenum, i; short *nts; Node kids[10]; p = reuse(p, nt); rulenum = getrule(p, nt); nts = IR->x._nts[rulenum]; fprint(stderr, "dumpcover(%x) = ", p); for (i = 0; i < in; i++) fprint(stderr, " "); dumprule(rulenum); (*IR->x._kids)(p, rulenum, kids); for (i = 0; nts[i]; i++) dumpcover(kids[i], nts[i], in+1); } static void dumprule(int rulenum) { assert(rulenum); fprint(stderr, "%s / %s", IR->x._string[rulenum], IR->x._templates[rulenum]); if (!IR->x._isinstruction[rulenum]) fprint(stderr, "\n"); } static unsigned emitasm(Node p, int nt) { int rulenum; short *nts; char *fmt; Node kids[10]; p = reuse(p, nt); rulenum = getrule(p, nt); nts = IR->x._nts[rulenum]; fmt = IR->x._templates[rulenum]; assert(fmt); if (IR->x._isinstruction[rulenum] && p->x.emitted) print("%s", p->syms[RX]->x.name); else if (*fmt == '#') (*IR->x.emit2)(p); else { if (*fmt == '?') { fmt++; assert(p->kids[0]); if (p->syms[RX] == p->x.kids[0]->syms[RX]) while (*fmt++ != '\n') ; } for ((*IR->x._kids)(p, rulenum, kids); *fmt; fmt++) if (*fmt != '%') (void)putchar(*fmt); else if (*++fmt == 'F') print("%d", framesize); else if (*fmt >= '0' && *fmt <= '9') emitasm(kids[*fmt - '0'], nts[*fmt - '0']); else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms)) fputs(p->syms[*fmt - 'a']->x.name, stdout); else (void)putchar(*fmt); } return 0; } void emit(Node p) { for (; p; p = p->x.next) { assert(p->x.registered); if ((p->x.equatable && requate(p)) || moveself(p)) ; else (*emitter)(p, p->x.inst); p->x.emitted = 1; } } static int moveself(Node p) { return p->x.copy && p->syms[RX]->x.name == p->x.kids[0]->syms[RX]->x.name; } int move(Node p) { p->x.copy = 1; return 1; } static int requate(Node q) { Symbol src = q->x.kids[0]->syms[RX]; Symbol tmp = q->syms[RX]; Node p; int n = 0; debug(fprint(stderr, "(requate(%x): tmp=%s src=%s)\n", q, tmp->x.name, src->x.name)); for (p = q->x.next; p; p = p->x.next) if (p->x.copy && p->syms[RX] == src && p->x.kids[0]->syms[RX] == tmp) debug(fprint(stderr, "(requate arm 0 at %x)\n", p)), p->syms[RX] = tmp; else if (setsrc(p->syms[RX]) && !moveself(p) && !readsreg(p)) return 0; else if (p->x.spills) return 0; else if (generic(p->op) == CALL && p->x.next) return 0; else if (p->op == LABEL+V && p->x.next) return 0; else if (p->syms[RX] == tmp && readsreg(p)) debug(fprint(stderr, "(requate arm 5 at %x)\n", p)), n++; else if (p->syms[RX] == tmp) break; debug(fprint(stderr, "(requate arm 7 at %x)\n", p)); assert(n > 0); for (p = q->x.next; p; p = p->x.next) if (p->syms[RX] == tmp && readsreg(p)) { p->syms[RX] = src; if (--n <= 0) break; } return 1; } static void prelabel(Node p) { if (p == NULL) return; prelabel(p->kids[0]); prelabel(p->kids[1]); if (NeedsReg[opindex(p->op)]) setreg(p, (*IR->x.rmap)(opkind(p->op))); switch (generic(p->op)) { case ADDRF: case ADDRL: if (p->syms[0]->sclass == REGISTER) p->op = VREG+P; break; case INDIR: if (p->kids[0]->op == VREG+P) setreg(p, p->kids[0]->syms[0]); break; case ASGN: if (p->kids[0]->op == VREG+P) rtarget(p, 1, p->kids[0]->syms[0]); break; case CVI: case CVU: case CVP: if (optype(p->op) != F && opsize(p->op) <= p->syms[0]->u.c.v.i) p->op = LOAD + opkind(p->op); break; } (IR->x.target)(p); } void setreg(Node p, Symbol r) { p->syms[RX] = r; } void rtarget(Node p, int n, Symbol r) { Node q = p->kids[n]; assert(q); assert(r); assert(r->sclass == REGISTER || !r->x.wildcard); assert(q->syms[RX]); if (r != q->syms[RX] && !q->syms[RX]->x.wildcard) { q = newnode(LOAD + opkind(q->op), q, NULL, q->syms[0]); if (r->u.t.cse == p->kids[n]) r->u.t.cse = q; p->kids[n] = p->x.kids[n] = q; q->x.kids[0] = q->kids[0]; } setreg(q, r); debug(fprint(stderr, "(targeting %x->x.kids[%d]=%x to %s)\n", p, n, p->kids[n], r->x.name)); } static void rewrite(Node p) { assert(p->x.inst == 0); prelabel(p); debug(dumptree(p)); debug(fprint(stderr, "\n")); (*IR->x._label)(p); debug(dumpcover(p, 1, 0)); reduce(p, 1); } Node gen(Node forest) { int i; struct node sentinel; Node dummy, p; head = forest; for (p = forest; p; p = p->link) { assert(p->count == 0); if (generic(p->op) == CALL) docall(p); else if ( generic(p->op) == ASGN && generic(p->kids[1]->op) == CALL) docall(p->kids[1]); else if (generic(p->op) == ARG) (*IR->x.doarg)(p); rewrite(p); p->x.listed = 1; } for (p = forest; p; p = p->link) prune(p, &dummy); relink(&sentinel, &sentinel); for (p = forest; p; p = p->link) linearize(p, &sentinel); forest = sentinel.x.next; assert(forest); sentinel.x.next->x.prev = NULL; sentinel.x.prev->x.next = NULL; for (p = forest; p; p = p->x.next) for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { assert(p->x.kids[i]->syms[RX]); if (p->x.kids[i]->syms[RX]->temporary) { p->x.kids[i]->x.prevuse = p->x.kids[i]->syms[RX]->x.lastuse; p->x.kids[i]->syms[RX]->x.lastuse = p->x.kids[i]; } } for (p = forest; p; p = p->x.next) { ralloc(p); if (p->x.listed && NeedsReg[opindex(p->op)] && (*IR->x.rmap)(opkind(p->op))) { assert(generic(p->op) == CALL || generic(p->op) == LOAD); putreg(p->syms[RX]); } } return forest; } int notarget(Node p) { return p->syms[RX]->x.wildcard ? 0 : LBURG_MAX; } static void putreg(Symbol r) { assert(r && r->x.regnode); freemask[r->x.regnode->set] |= r->x.regnode->mask; debug(dumpregs("(freeing %s)\n", r->x.name, NULL)); } static Symbol askfixedreg(Symbol s) { Regnode r = s->x.regnode; int n = r->set; if (r->mask&~freemask[n]) return NULL; else { freemask[n] &= ~r->mask; usedmask[n] |= r->mask; return s; } } static Symbol askreg(Symbol rs, unsigned rmask[]) { int i; if (rs->x.wildcard == NULL) return askfixedreg(rs); for (i = 31; i >= 0; i--) { Symbol r = rs->x.wildcard[i]; if (r != NULL && !(r->x.regnode->mask&~rmask[r->x.regnode->set]) && askfixedreg(r)) return r; } return NULL; } static Symbol getreg(Symbol s, unsigned mask[], Node p) { Symbol r = askreg(s, mask); if (r == NULL) { r = spillee(s, mask, p); assert(r && r->x.regnode); spill(r->x.regnode->mask, r->x.regnode->set, p); r = askreg(s, mask); } assert(r && r->x.regnode); r->x.regnode->vbl = NULL; return r; } int askregvar(Symbol p, Symbol regs) { Symbol r; assert(p); if (p->sclass != REGISTER) return 0; else if (!isscalar(p->type)) { p->sclass = AUTO; return 0; } else if (p->temporary) { p->x.name = "?"; return 1; } else if ((r = askreg(regs, vmask)) != NULL) { p->x.regnode = r->x.regnode; p->x.regnode->vbl = p; p->x.name = r->x.name; debug(dumpregs("(allocating %s to symbol %s)\n", p->x.name, p->name)); return 1; } else { p->sclass = AUTO; return 0; } } static void linearize(Node p, Node next) { int i; for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) linearize(p->x.kids[i], next); relink(next->x.prev, p); relink(p, next); debug(fprint(stderr, "(listing %x)\n", p)); } static void ralloc(Node p) { int i; unsigned mask[2]; mask[0] = tmask[0]; mask[1] = tmask[1]; assert(p); debug(fprint(stderr, "(rallocing %x)\n", p)); for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { Node kid = p->x.kids[i]; Symbol r = kid->syms[RX]; assert(r && kid->x.registered); if (r->sclass != REGISTER && r->x.lastuse == kid) putreg(r); } if (!p->x.registered && NeedsReg[opindex(p->op)] && (*IR->x.rmap)(opkind(p->op))) { Symbol sym = p->syms[RX], set = sym; assert(sym); if (sym->temporary) set = (*IR->x.rmap)(opkind(p->op)); assert(set); if (set->sclass != REGISTER) { Symbol r; if (*IR->x._templates[getrule(p, p->x.inst)] == '?') for (i = 1; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { Symbol r = p->x.kids[i]->syms[RX]; assert(p->x.kids[i]->x.registered); assert(r && r->x.regnode); assert(sym->x.wildcard || sym != r); mask[r->x.regnode->set] &= ~r->x.regnode->mask; } r = getreg(set, mask, p); if (sym->temporary) { Node q; r->x.lastuse = sym->x.lastuse; for (q = sym->x.lastuse; q; q = q->x.prevuse) { q->syms[RX] = r; q->x.registered = 1; if (sym->u.t.cse && q->x.copy) q->x.equatable = 1; } } else { p->syms[RX] = r; r->x.lastuse = p; } debug(dumpregs("(allocating %s to node %x)\n", r->x.name, (char *) p)); } } p->x.registered = 1; (*IR->x.clobber)(p); } static Symbol spillee(Symbol set, unsigned mask[], Node here) { Symbol bestreg = NULL; int bestdist = -1, i; assert(set); if (!set->x.wildcard) bestreg = set; else { for (i = 31; i >= 0; i--) { Symbol ri = set->x.wildcard[i]; if ( ri != NULL && ri->x.lastuse && (ri->x.regnode->mask&tmask[ri->x.regnode->set]&mask[ri->x.regnode->set]) ) { Regnode rn = ri->x.regnode; Node q = here; int dist = 0; for (; q && !uses(q, rn); q = q->x.next) dist++; if (q && dist > bestdist) { bestdist = dist; bestreg = ri; } } } } assert(bestreg); /* Must be able to spill something. Reconfigure the register allocator to ensure that we can allocate a register for all nodes without spilling the node's necessary input regs. */ assert(bestreg->x.regnode->vbl == NULL); /* Can't spill register variables because the reload site might be in other blocks. Reconfigure the register allocator to ensure that this register is never allocated to a variable. */ return bestreg; } static int uses(Node p, Regnode rn) { int i; for (i = 0; i < NELEMS(p->x.kids); i++) if ( p->x.kids[i] && p->x.kids[i]->x.registered && rn->set == p->x.kids[i]->syms[RX]->x.regnode->set && (rn->mask&p->x.kids[i]->syms[RX]->x.regnode->mask) ) return 1; return 0; } static void spillr(Symbol r, Node here) { int i; Symbol tmp; Node p = r->x.lastuse; assert(p); while (p->x.prevuse) assert(r == p->syms[RX]), p = p->x.prevuse; assert(p->x.registered && !readsreg(p)); tmp = newtemp(AUTO, optype(p->op), opsize(p->op)); genspill(r, p, tmp); for (p = here->x.next; p; p = p->x.next) for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { Node k = p->x.kids[i]; if (k->x.registered && k->syms[RX] == r) genreload(p, tmp, i); } putreg(r); } static void genspill(Symbol r, Node last, Symbol tmp) { Node p, q; Symbol s; unsigned ty; debug(fprint(stderr, "(spilling %s to local %s)\n", r->x.name, tmp->x.name)); debug(fprint(stderr, "(genspill: ")); debug(dumptree(last)); debug(fprint(stderr, ")\n")); ty = opkind(last->op); NEW0(s, FUNC); s->sclass = REGISTER; s->name = s->x.name = r->x.name; s->x.regnode = r->x.regnode; q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, s); q = newnode(INDIR + ty, q, NULL, NULL); p = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp); p = newnode(ASGN + ty, p, q, NULL); p->x.spills = 1; rewrite(p); prune(p, &q); q = last->x.next; linearize(p, q); for (p = last->x.next; p != q; p = p->x.next) { ralloc(p); assert(!p->x.listed || !NeedsReg[opindex(p->op)] || !(*IR->x.rmap)(opkind(p->op))); } } static void genreload(Node p, Symbol tmp, int i) { Node q; int ty; debug(fprint(stderr, "(replacing %x with a reload from %s)\n", p->x.kids[i], tmp->x.name)); debug(fprint(stderr, "(genreload: ")); debug(dumptree(p->x.kids[i])); debug(fprint(stderr, ")\n")); ty = opkind(p->x.kids[i]->op); q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp); p->x.kids[i] = newnode(INDIR + ty, q, NULL, NULL); rewrite(p->x.kids[i]); prune(p->x.kids[i], &q); reprune(&p->kids[1], reprune(&p->kids[0], 0, i, p), i, p); prune(p, &q); linearize(p->x.kids[i], p); } static int reprune(Node *pp, int k, int n, Node p) { struct node x, *q = *pp; if (q == NULL || k > n) return k; else if (q->x.inst == 0) return reprune(&q->kids[1], reprune(&q->kids[0], k, n, p), n, p); if (k == n) { debug(fprint(stderr, "(reprune changes %x from %x to %x)\n", pp, *pp, p->x.kids[n])); *pp = p->x.kids[n]; x = *p; (IR->x.target)(&x); } return k + 1; } void spill(unsigned mask, int n, Node here) { int i; Node p; here->x.spills = 1; usedmask[n] |= mask; if (mask&~freemask[n]) { assert( /* It makes no sense for a node to clobber() its target. */ here->x.registered == 0 || /* call isn't coming through clobber() */ here->syms[RX] == NULL || here->syms[RX]->x.regnode == NULL || here->syms[RX]->x.regnode->set != n || (here->syms[RX]->x.regnode->mask&mask) == 0 ); for (p = here; p; p = p->x.next) for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) { Symbol r = p->x.kids[i]->syms[RX]; assert(r); if (p->x.kids[i]->x.registered && r->x.regnode->set == n && r->x.regnode->mask&mask) spillr(r, here); } } } static void dumpregs(char *msg, char *a, char *b) { fprint(stderr, msg, a, b); fprint(stderr, "(free[0]=%x)\n", freemask[0]); fprint(stderr, "(free[1]=%x)\n", freemask[1]); } int getregnum(Node p) { assert(p && p->syms[RX] && p->syms[RX]->x.regnode); return p->syms[RX]->x.regnode->number; } unsigned regloc(Symbol p) { assert(p && p->sclass == REGISTER && p->sclass == REGISTER && p->x.regnode); return p->x.regnode->set<<8 | p->x.regnode->number; } openarena_0.8.8.orig/code/tools/lcc/src/bytecode.c0000644000175000017500000001772011656310262020577 0ustar smcvsmcv#include "c.h" #define I(f) b_##f static void I(segment)(int n) { static int cseg; if (cseg != n) switch (cseg = n) { case CODE: print("code\n"); return; case DATA: print("data\n"); return; case BSS: print("bss\n"); return; case LIT: print("lit\n"); return; default: assert(0); } } static void I(address)(Symbol q, Symbol p, long n) { q->x.name = stringf("%s%s%D", p->x.name, n > 0 ? "+" : "", n); } static void I(defaddress)(Symbol p) { print("address %s\n", p->x.name); } static void I(defconst)(int suffix, int size, Value v) { switch (suffix) { case I: if (size > sizeof (int)) print("byte %d %D\n", size, v.i); else print("byte %d %d\n", size, v.i); return; case U: if (size > sizeof (unsigned)) print("byte %d %U\n", size, v.u); else print("byte %d %u\n", size, v.u); return; case P: print("byte %d %U\n", size, (unsigned long)v.p); return; case F: if (size == 4) { floatint_t fi; fi.f = v.d; print("byte 4 %u\n", fi.ui); } else { unsigned *p = (unsigned *)&v.d; print("byte 4 %u\n", p[swap]); print("byte 4 %u\n", p[1 - swap]); } return; } assert(0); } static void I(defstring)(int len, char *str) { char *s; for (s = str; s < str + len; s++) print("byte 1 %d\n", (*s)&0377); } static void I(defsymbol)(Symbol p) { if (p->scope == CONSTANTS) switch (optype(ttob(p->type))) { case I: p->x.name = stringf("%D", p->u.c.v.i); break; case U: p->x.name = stringf("%U", p->u.c.v.u); break; case P: p->x.name = stringf("%U", p->u.c.v.p); break; case F: { // JDC: added this to get inline floats floatint_t temp; temp.f = p->u.c.v.d; p->x.name = stringf("%U", temp.ui ); } break;// JDC: added this default: assert(0); } else if (p->scope >= LOCAL && p->sclass == STATIC) p->x.name = stringf("$%d", genlabel(1)); else if (p->scope == LABELS || p->generated) p->x.name = stringf("$%s", p->name); else p->x.name = p->name; } static void dumptree(Node p) { switch (specific(p->op)) { case ASGN+B: assert(p->kids[0]); assert(p->kids[1]); assert(p->syms[0]); dumptree(p->kids[0]); dumptree(p->kids[1]); print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.u); return; case RET+V: assert(!p->kids[0]); assert(!p->kids[1]); print("%s\n", opname(p->op)); return; } switch (generic(p->op)) { case CNST: case ADDRG: case ADDRF: case ADDRL: case LABEL: assert(!p->kids[0]); assert(!p->kids[1]); assert(p->syms[0] && p->syms[0]->x.name); print("%s %s\n", opname(p->op), p->syms[0]->x.name); return; case CVF: case CVI: case CVP: case CVU: assert(p->kids[0]); assert(!p->kids[1]); assert(p->syms[0]); dumptree(p->kids[0]); print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.i); return; case ARG: case BCOM: case NEG: case INDIR: case JUMP: case RET: assert(p->kids[0]); assert(!p->kids[1]); dumptree(p->kids[0]); print("%s\n", opname(p->op)); return; case CALL: assert(p->kids[0]); assert(!p->kids[1]); assert(optype(p->op) != B); dumptree(p->kids[0]); print("%s\n", opname(p->op)); if ( !p->count ) { printf("pop\n"); }; // JDC return; case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH: case ADD: case SUB: case DIV: case MUL: case MOD: assert(p->kids[0]); assert(p->kids[1]); dumptree(p->kids[0]); dumptree(p->kids[1]); print("%s\n", opname(p->op)); return; case EQ: case NE: case GT: case GE: case LE: case LT: assert(p->kids[0]); assert(p->kids[1]); assert(p->syms[0]); assert(p->syms[0]->x.name); dumptree(p->kids[0]); dumptree(p->kids[1]); print("%s %s\n", opname(p->op), p->syms[0]->x.name); return; } assert(0); } static void I(emit)(Node p) { for (; p; p = p->link) dumptree(p); } static void I(export)(Symbol p) { print("export %s\n", p->x.name); } static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) { int i; (*IR->segment)(CODE); offset = 0; for (i = 0; caller[i] && callee[i]; i++) { offset = roundup(offset, caller[i]->type->align); caller[i]->x.name = callee[i]->x.name = stringf("%d", offset); caller[i]->x.offset = callee[i]->x.offset = offset; offset += caller[i]->type->size; } maxargoffset = maxoffset = argoffset = offset = 0; gencode(caller, callee); print("proc %s %d %d\n", f->x.name, maxoffset, maxargoffset); emitcode(); print("endproc %s %d %d\n", f->x.name, maxoffset, maxargoffset); } static void gen02(Node p) { assert(p); if (generic(p->op) == ARG) { assert(p->syms[0]); argoffset += (p->syms[0]->u.c.v.i < 4 ? 4 : p->syms[0]->u.c.v.i); } else if (generic(p->op) == CALL) { maxargoffset = (argoffset > maxargoffset ? argoffset : maxargoffset); argoffset = 0; } } static void gen01(Node p) { if (p) { gen01(p->kids[0]); gen01(p->kids[1]); gen02(p); } } static Node I(gen)(Node p) { Node q; assert(p); for (q = p; q; q = q->link) gen01(q); return p; } static void I(global)(Symbol p) { print("align %d\n", p->type->align > 4 ? 4 : p->type->align); print("LABELV %s\n", p->x.name); } static void I(import)(Symbol p) { print("import %s\n", p->x.name); } static void I(local)(Symbol p) { offset = roundup(offset, p->type->align); p->x.name = stringf("%d", offset); p->x.offset = offset; offset += p->type->size; } static void I(progbeg)(int argc, char *argv[]) {} static void I(progend)(void) {} static void I(space)(int n) { print("skip %d\n", n); } //======================================================== // JDC: hacked up to get interleaved source lines in asm code static char *sourceFile; static char *sourcePtr; static int sourceLine; static int filelength( FILE *f ) { int pos; int end; pos = ftell (f); fseek (f, 0, SEEK_END); end = ftell (f); fseek (f, pos, SEEK_SET); return end; } static void LoadSourceFile( const char *filename ) { FILE *f; int length; f = fopen( filename, "r" ); if ( !f ) { print( ";couldn't open %s\n", filename ); sourceFile = NULL; return; } length = filelength( f ); sourceFile = malloc( length + 1 ); if ( sourceFile ) { size_t size; size = fread( sourceFile, length, 1, f ); sourceFile[length] = 0; } fclose( f ); sourceLine = 1; sourcePtr = sourceFile; } static void PrintToSourceLine( int line ) { int c; if ( !sourceFile ) { return; } while ( sourceLine <= line ) { int i; for ( i = 0 ; sourcePtr[i] && sourcePtr[i] != '\n' ; i++ ) { } c = sourcePtr[i]; if ( c == '\n' ) { sourcePtr[i] = 0; } print( ";%d:%s\n", sourceLine, sourcePtr ); if ( c == 0 ) { sourcePtr += i; // end of file } else { sourcePtr += i+1; } sourceLine++; } } static void I(stabline)(Coordinate *cp) { static char *prevfile; static int prevline; if (cp->file && (prevfile == NULL || strcmp(prevfile, cp->file) != 0)) { print("file \"%s\"\n", prevfile = cp->file); prevline = 0; if ( sourceFile ) { free( sourceFile ); sourceFile = NULL; } // load the new source file LoadSourceFile( cp->file ); } if (cp->y != prevline) { print("line %d\n", prevline = cp->y); PrintToSourceLine( cp->y ); } } //======================================================== #define b_blockbeg blockbeg #define b_blockend blockend Interface bytecodeIR = { {1, 1, 0}, /* char */ {2, 2, 0}, /* short */ {4, 4, 0}, /* int */ {4, 4, 0}, /* long */ {4, 4, 0}, /* long long */ {4, 4, 0}, /* float */ // JDC: use inline floats {4, 4, 0}, /* double */ // JDC: don't ever emit 8 byte double code {4, 4, 0}, /* long double */ // JDC: don't ever emit 8 byte double code {4, 4, 0}, /* T* */ {0, 4, 0}, /* struct */ 0, /* little_endian */ 0, /* mulops_calls */ 0, /* wants_callb */ 0, /* wants_argb */ 1, /* left_to_right */ 0, /* wants_dag */ 0, /* unsigned_char */ I(address), I(blockbeg), I(blockend), I(defaddress), I(defconst), I(defstring), I(defsymbol), I(emit), I(export), I(function), I(gen), I(global), I(import), I(local), I(progbeg), I(progend), I(segment), I(space), 0, /* I(stabblock) */ 0, /* I(stabend) */ 0, /* I(stabfend) */ 0, /* I(stabinit) */ I(stabline), 0, /* I(stabsym) */ 0, /* I(stabtype) */ }; openarena_0.8.8.orig/code/tools/lcc/src/null.c0000644000175000017500000000361111656310262017745 0ustar smcvsmcv#include "c.h" #define I(f) null_##f static Node I(gen)(Node p) { return p; } static void I(address)(Symbol q, Symbol p, long n) {} static void I(blockbeg)(Env *e) {} static void I(blockend)(Env *e) {} static void I(defaddress)(Symbol p) {} static void I(defconst)(int suffix, int size, Value v) {} static void I(defstring)(int len, char *s) {} static void I(defsymbol)(Symbol p) {} static void I(emit)(Node p) {} static void I(export)(Symbol p) {} static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) {} static void I(global)(Symbol p) {} static void I(import)(Symbol p) {} static void I(local)(Symbol p) {} static void I(progbeg)(int argc, char *argv[]) {} static void I(progend)(void) {} static void I(segment)(int s) {} static void I(space)(int n) {} static void I(stabblock)(int brace, int lev, Symbol *p) {} static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) {} static void I(stabfend)(Symbol p, int lineno) {} static void I(stabinit)(char *file, int argc, char *argv[]) {} static void I(stabline)(Coordinate *cp) {} static void I(stabsym)(Symbol p) {} static void I(stabtype)(Symbol p) {} Interface nullIR = { {1, 1, 0}, /* char */ {2, 2, 0}, /* short */ {4, 4, 0}, /* int */ {8, 8, 1}, /* long */ {8 ,8, 1}, /* long long */ {4, 4, 1}, /* float */ {8, 8, 1}, /* double */ {16,16,1}, /* long double */ {4, 4, 0}, /* T* */ {0, 4, 0}, /* struct */ 1, /* little_endian */ 0, /* mulops_calls */ 0, /* wants_callb */ 0, /* wants_argb */ 1, /* left_to_right */ 0, /* wants_dag */ 0, /* unsigned_char */ I(address), I(blockbeg), I(blockend), I(defaddress), I(defconst), I(defstring), I(defsymbol), I(emit), I(export), I(function), I(gen), I(global), I(import), I(local), I(progbeg), I(progend), I(segment), I(space), I(stabblock), I(stabend), I(stabfend), I(stabinit), I(stabline), I(stabsym), I(stabtype) }; openarena_0.8.8.orig/code/tools/lcc/src/inits.c0000644000175000017500000000055211656310263020123 0ustar smcvsmcvvoid init(int argc, char *argv[]) { {extern void input_init(int, char *[]); input_init(argc, argv);} {extern void main_init(int, char *[]); main_init(argc, argv);} {extern void prof_init(int, char *[]); prof_init(argc, argv);} {extern void trace_init(int, char *[]); trace_init(argc, argv);} {extern void type_init(int, char *[]); type_init(argc, argv);} } openarena_0.8.8.orig/code/tools/lcc/src/stmt.c0000644000175000017500000004131711656310262017767 0ustar smcvsmcv#include "c.h" #define SWSIZE 512 #define den(i,j) ((j-buckets[i]+1.0)/(v[j]-v[buckets[i]]+1)) struct code codehead = { Start }; Code codelist = &codehead; float density = 0.5; Table stmtlabs; static int foldcond(Tree e1, Tree e2); static void caselabel(Swtch, long, int); static void cmp(int, Symbol, long, int); static Tree conditional(int); static void dostmt(int, Swtch, int); static int equal(Symbol, Symbol); static void forstmt(int, Swtch, int); static void ifstmt(int, int, Swtch, int); static Symbol localaddr(Tree); static void stmtlabel(void); static void swstmt(int, int, int); static void whilestmt(int, Swtch, int); Code code(int kind) { Code cp; if (!reachable(kind)) warning("unreachable code\n"); NEW(cp, FUNC); cp->kind = kind; cp->prev = codelist; cp->next = NULL; codelist->next = cp; codelist = cp; return cp; } int reachable(int kind) { if (kind > Start) { Code cp; for (cp = codelist; cp->kind < Label; ) cp = cp->prev; if (cp->kind == Jump || cp->kind == Switch) return 0; } return 1; } void addlocal(Symbol p) { if (!p->defined) { code(Local)->u.var = p; p->defined = 1; p->scope = level; } } void definept(Coordinate *p) { Code cp = code(Defpoint); cp->u.point.src = p ? *p : src; cp->u.point.point = npoints; if (ncalled > 0) { int n = findcount(cp->u.point.src.file, cp->u.point.src.x, cp->u.point.src.y); if (n > 0) refinc = (float)n/ncalled; } if (glevel > 2) locus(identifiers, &cp->u.point.src); if (events.points && reachable(Gen)) { Tree e = NULL; apply(events.points, &cp->u.point.src, &e); if (e) listnodes(e, 0, 0); } } void statement(int loop, Swtch swp, int lev) { float ref = refinc; if (Aflag >= 2 && lev == 15) warning("more than 15 levels of nested statements\n"); switch (t) { case IF: ifstmt(genlabel(2), loop, swp, lev + 1); break; case WHILE: whilestmt(genlabel(3), swp, lev + 1); break; case DO: dostmt(genlabel(3), swp, lev + 1); expect(';'); break; case FOR: forstmt(genlabel(4), swp, lev + 1); break; case BREAK: walk(NULL, 0, 0); definept(NULL); if (swp && swp->lab > loop) branch(swp->lab + 1); else if (loop) branch(loop + 2); else error("illegal break statement\n"); t = gettok(); expect(';'); break; case CONTINUE: walk(NULL, 0, 0); definept(NULL); if (loop) branch(loop + 1); else error("illegal continue statement\n"); t = gettok(); expect(';'); break; case SWITCH: swstmt(loop, genlabel(2), lev + 1); break; case CASE: { int lab = genlabel(1); if (swp == NULL) error("illegal case label\n"); definelab(lab); while (t == CASE) { static char stop[] = { IF, ID, 0 }; Tree p; t = gettok(); p = constexpr(0); if (generic(p->op) == CNST && isint(p->type)) { if (swp) { needconst++; p = cast(p, swp->sym->type); if (p->type->op == UNSIGNED) p->u.v.i = extend(p->u.v.u, p->type); needconst--; caselabel(swp, p->u.v.i, lab); } } else error("case label must be a constant integer expression\n"); test(':', stop); } statement(loop, swp, lev); } break; case DEFAULT: if (swp == NULL) error("illegal default label\n"); else if (swp->deflab) error("extra default label\n"); else { swp->deflab = findlabel(swp->lab); definelab(swp->deflab->u.l.label); } t = gettok(); expect(':'); statement(loop, swp, lev); break; case RETURN: { Type rty = freturn(cfunc->type); t = gettok(); definept(NULL); if (t != ';') if (rty == voidtype) { error("extraneous return value\n"); expr(0); retcode(NULL); } else retcode(expr(0)); else { if (rty != voidtype) warning("missing return value\n"); retcode(NULL); } branch(cfunc->u.f.label); } expect(';'); break; case '{': compound(loop, swp, lev + 1); break; case ';': definept(NULL); t = gettok(); break; case GOTO: walk(NULL, 0, 0); definept(NULL); t = gettok(); if (t == ID) { Symbol p = lookup(token, stmtlabs); if (p == NULL) { p = install(token, &stmtlabs, 0, FUNC); p->scope = LABELS; p->u.l.label = genlabel(1); p->src = src; } use(p, src); branch(p->u.l.label); t = gettok(); } else error("missing label in goto\n"); expect(';'); break; case ID: if (getchr() == ':') { stmtlabel(); statement(loop, swp, lev); break; } default: definept(NULL); if (kind[t] != ID) { error("unrecognized statement\n"); t = gettok(); } else { Tree e = expr0(0); listnodes(e, 0, 0); if (nodecount == 0 || nodecount > 200) walk(NULL, 0, 0); else if (glevel) walk(NULL, 0, 0); deallocate(STMT); } expect(';'); break; } if (kind[t] != IF && kind[t] != ID && t != '}' && t != EOI) { static char stop[] = { IF, ID, '}', 0 }; error("illegal statement termination\n"); skipto(0, stop); } refinc = ref; } static void ifstmt(int lab, int loop, Swtch swp, int lev) { t = gettok(); expect('('); definept(NULL); walk(conditional(')'), 0, lab); refinc /= 2.0; statement(loop, swp, lev); if (t == ELSE) { branch(lab + 1); t = gettok(); definelab(lab); statement(loop, swp, lev); if (findlabel(lab + 1)->ref) definelab(lab + 1); } else definelab(lab); } static Tree conditional(int tok) { Tree p = expr(tok); if (Aflag > 1 && isfunc(p->type)) warning("%s used in a conditional expression\n", funcname(p)); return cond(p); } static void stmtlabel(void) { Symbol p = lookup(token, stmtlabs); if (p == NULL) { p = install(token, &stmtlabs, 0, FUNC); p->scope = LABELS; p->u.l.label = genlabel(1); p->src = src; } if (p->defined) error("redefinition of label `%s' previously defined at %w\n", p->name, &p->src); p->defined = 1; definelab(p->u.l.label); t = gettok(); expect(':'); } static void forstmt(int lab, Swtch swp, int lev) { int once = 0; Tree e1 = NULL, e2 = NULL, e3 = NULL; Coordinate pt2, pt3; t = gettok(); expect('('); definept(NULL); if (kind[t] == ID) e1 = texpr(expr0, ';', FUNC); else expect(';'); walk(e1, 0, 0); pt2 = src; refinc *= 10.0; if (kind[t] == ID) e2 = texpr(conditional, ';', FUNC); else expect(';'); pt3 = src; if (kind[t] == ID) e3 = texpr(expr0, ')', FUNC); else { static char stop[] = { IF, ID, '}', 0 }; test(')', stop); } if (e2) { once = foldcond(e1, e2); if (!once) branch(lab + 3); } definelab(lab); statement(lab, swp, lev); definelab(lab + 1); definept(&pt3); if (e3) walk(e3, 0, 0); if (e2) { if (!once) definelab(lab + 3); definept(&pt2); walk(e2, lab, 0); } else { definept(&pt2); branch(lab); } if (findlabel(lab + 2)->ref) definelab(lab + 2); } static void swstmt(int loop, int lab, int lev) { Tree e; struct swtch sw; Code head, tail; t = gettok(); expect('('); definept(NULL); e = expr(')'); if (!isint(e->type)) { error("illegal type `%t' in switch expression\n", e->type); e = retype(e, inttype); } e = cast(e, promote(e->type)); if (generic(e->op) == INDIR && isaddrop(e->kids[0]->op) && e->kids[0]->u.sym->type == e->type && !isvolatile(e->kids[0]->u.sym->type)) { sw.sym = e->kids[0]->u.sym; walk(NULL, 0, 0); } else { sw.sym = genident(REGISTER, e->type, level); addlocal(sw.sym); walk(asgn(sw.sym, e), 0, 0); } head = code(Switch); sw.lab = lab; sw.deflab = NULL; sw.ncases = 0; sw.size = SWSIZE; sw.values = newarray(SWSIZE, sizeof *sw.values, FUNC); sw.labels = newarray(SWSIZE, sizeof *sw.labels, FUNC); refinc /= 10.0; statement(loop, &sw, lev); if (sw.deflab == NULL) { sw.deflab = findlabel(lab); definelab(lab); if (sw.ncases == 0) warning("switch statement with no cases\n"); } if (findlabel(lab + 1)->ref) definelab(lab + 1); tail = codelist; codelist = head->prev; codelist->next = head->prev = NULL; if (sw.ncases > 0) swgen(&sw); branch(lab); head->next->prev = codelist; codelist->next = head->next; codelist = tail; } static void caselabel(Swtch swp, long val, int lab) { int k; if (swp->ncases >= swp->size) { long *vals = swp->values; Symbol *labs = swp->labels; swp->size *= 2; swp->values = newarray(swp->size, sizeof *swp->values, FUNC); swp->labels = newarray(swp->size, sizeof *swp->labels, FUNC); for (k = 0; k < swp->ncases; k++) { swp->values[k] = vals[k]; swp->labels[k] = labs[k]; } } k = swp->ncases; for ( ; k > 0 && swp->values[k-1] >= val; k--) { swp->values[k] = swp->values[k-1]; swp->labels[k] = swp->labels[k-1]; } if (k < swp->ncases && swp->values[k] == val) error("duplicate case label `%d'\n", val); swp->values[k] = val; swp->labels[k] = findlabel(lab); ++swp->ncases; if (Aflag >= 2 && swp->ncases == 258) warning("more than 257 cases in a switch\n"); } void swgen(Swtch swp) { int *buckets, k, n; long *v = swp->values; buckets = newarray(swp->ncases + 1, sizeof *buckets, FUNC); for (n = k = 0; k < swp->ncases; k++, n++) { buckets[n] = k; while (n > 0 && den(n-1, k) >= density) n--; } buckets[n] = swp->ncases; swcode(swp, buckets, 0, n - 1); } void swcode(Swtch swp, int b[], int lb, int ub) { int hilab, lolab, l, u, k = (lb + ub)/2; long *v = swp->values; if (k > lb && k < ub) { lolab = genlabel(1); hilab = genlabel(1); } else if (k > lb) { lolab = genlabel(1); hilab = swp->deflab->u.l.label; } else if (k < ub) { lolab = swp->deflab->u.l.label; hilab = genlabel(1); } else lolab = hilab = swp->deflab->u.l.label; l = b[k]; u = b[k+1] - 1; if (u - l + 1 <= 3) { int i; for (i = l; i <= u; i++) cmp(EQ, swp->sym, v[i], swp->labels[i]->u.l.label); if (k > lb && k < ub) cmp(GT, swp->sym, v[u], hilab); else if (k > lb) cmp(GT, swp->sym, v[u], hilab); else if (k < ub) cmp(LT, swp->sym, v[l], lolab); else assert(lolab == hilab), branch(lolab); walk(NULL, 0, 0); } else { Tree e; Type ty = signedint(swp->sym->type); Symbol table = genident(STATIC, array(voidptype, u - l + 1, 0), GLOBAL); (*IR->defsymbol)(table); if (!isunsigned(swp->sym->type) || v[l] != 0) cmp(LT, swp->sym, v[l], lolab); cmp(GT, swp->sym, v[u], hilab); e = (*optree['-'])(SUB, cast(idtree(swp->sym), ty), cnsttree(ty, v[l])); if (e->type->size < unsignedptr->size) e = cast(e, unsignedlong); walk(tree(JUMP, voidtype, rvalue((*optree['+'])(ADD, pointer(idtree(table)), e)), NULL), 0, 0); code(Switch); codelist->u.swtch.table = table; codelist->u.swtch.sym = swp->sym; codelist->u.swtch.deflab = swp->deflab; codelist->u.swtch.size = u - l + 1; codelist->u.swtch.values = &v[l]; codelist->u.swtch.labels = &swp->labels[l]; if (v[u] - v[l] + 1 >= 10000) warning("switch generates a huge table\n"); } if (k > lb) { assert(lolab != swp->deflab->u.l.label); definelab(lolab); swcode(swp, b, lb, k - 1); } if (k < ub) { assert(hilab != swp->deflab->u.l.label); definelab(hilab); swcode(swp, b, k + 1, ub); } } static void cmp(int op, Symbol p, long n, int lab) { Type ty = signedint(p->type); listnodes(eqtree(op, cast(idtree(p), ty), cnsttree(ty, n)), lab, 0); } void retcode(Tree p) { Type ty; if (p == NULL) { if (events.returns) apply(events.returns, cfunc, NULL); return; } p = pointer(p); ty = assign(freturn(cfunc->type), p); if (ty == NULL) { error("illegal return type; found `%t' expected `%t'\n", p->type, freturn(cfunc->type)); return; } p = cast(p, ty); if (retv) { if (iscallb(p)) p = tree(RIGHT, p->type, tree(CALL+B, p->type, p->kids[0]->kids[0], idtree(retv)), rvalue(idtree(retv))); else p = asgntree(ASGN, rvalue(idtree(retv)), p); walk(p, 0, 0); if (events.returns) apply(events.returns, cfunc, rvalue(idtree(retv))); return; } if (events.returns) { Symbol t1 = genident(AUTO, p->type, level); addlocal(t1); walk(asgn(t1, p), 0, 0); apply(events.returns, cfunc, idtree(t1)); p = idtree(t1); } if (!isfloat(p->type)) p = cast(p, promote(p->type)); if (isptr(p->type)) { Symbol q = localaddr(p); if (q && (q->computed || q->generated)) warning("pointer to a %s is an illegal return value\n", q->scope == PARAM ? "parameter" : "local"); else if (q) warning("pointer to %s `%s' is an illegal return value\n", q->scope == PARAM ? "parameter" : "local", q->name); } walk(tree(mkop(RET,p->type), p->type, p, NULL), 0, 0); } void definelab(int lab) { Code cp; Symbol p = findlabel(lab); assert(lab); walk(NULL, 0, 0); code(Label)->u.forest = newnode(LABEL+V, NULL, NULL, p); for (cp = codelist->prev; cp->kind <= Label; ) cp = cp->prev; while ( cp->kind == Jump && cp->u.forest->kids[0] && specific(cp->u.forest->kids[0]->op) == ADDRG+P && cp->u.forest->kids[0]->syms[0] == p) { assert(cp->u.forest->kids[0]->syms[0]->u.l.label == lab); p->ref--; assert(cp->next); assert(cp->prev); cp->prev->next = cp->next; cp->next->prev = cp->prev; cp = cp->prev; while (cp->kind <= Label) cp = cp->prev; } } Node jump(int lab) { Symbol p = findlabel(lab); p->ref++; return newnode(JUMP+V, newnode(ADDRG+ttob(voidptype), NULL, NULL, p), NULL, NULL); } void branch(int lab) { Code cp; Symbol p = findlabel(lab); assert(lab); walk(NULL, 0, 0); code(Label)->u.forest = jump(lab); for (cp = codelist->prev; cp->kind < Label; ) cp = cp->prev; while ( cp->kind == Label && cp->u.forest->op == LABEL+V && !equal(cp->u.forest->syms[0], p)) { equatelab(cp->u.forest->syms[0], p); assert(cp->next); assert(cp->prev); cp->prev->next = cp->next; cp->next->prev = cp->prev; cp = cp->prev; while (cp->kind < Label) cp = cp->prev; } if (cp->kind == Jump || cp->kind == Switch) { p->ref--; codelist->prev->next = NULL; codelist = codelist->prev; } else { codelist->kind = Jump; if (cp->kind == Label && cp->u.forest->op == LABEL+V && equal(cp->u.forest->syms[0], p)) warning("source code specifies an infinite loop"); } } void equatelab(Symbol old, Symbol new) { assert(old->u.l.equatedto == NULL); old->u.l.equatedto = new; new->ref++; } static int equal(Symbol lprime, Symbol dst) { assert(dst && lprime); for ( ; dst; dst = dst->u.l.equatedto) if (lprime == dst) return 1; return 0; } /* dostmt - do statement while ( expression ) */ static void dostmt(int lab, Swtch swp, int lev) { refinc *= 10.0; t = gettok(); definelab(lab); statement(lab, swp, lev); definelab(lab + 1); expect(WHILE); expect('('); definept(NULL); walk(conditional(')'), lab, 0); if (findlabel(lab + 2)->ref) definelab(lab + 2); } /* foldcond - check if initial test in for(e1;e2;e3) S is necessary */ static int foldcond(Tree e1, Tree e2) { int op = generic(e2->op); Symbol v; if (e1 == 0 || e2 == 0) return 0; if (generic(e1->op) == ASGN && isaddrop(e1->kids[0]->op) && generic(e1->kids[1]->op) == CNST) { v = e1->kids[0]->u.sym; e1 = e1->kids[1]; } else return 0; if ((op==LE || op==LT || op==EQ || op==NE || op==GT || op==GE) && generic(e2->kids[0]->op) == INDIR && e2->kids[0]->kids[0]->u.sym == v && e2->kids[1]->op == e1->op) { e1 = simplify(op, e2->type, e1, e2->kids[1]); if (e1->op == CNST+I) return e1->u.v.i; } return 0; } /* localaddr - returns q if p yields the address of local/parameter q; otherwise returns 0 */ static Symbol localaddr(Tree p) { if (p == NULL) return NULL; switch (generic(p->op)) { case INDIR: case CALL: case ARG: return NULL; case ADDRL: case ADDRF: return p->u.sym; case RIGHT: case ASGN: if (p->kids[1]) return localaddr(p->kids[1]); return localaddr(p->kids[0]); case COND: { Symbol q; assert(p->kids[1] && p->kids[1]->op == RIGHT); if ((q = localaddr(p->kids[1]->kids[0])) != NULL) return q; return localaddr(p->kids[1]->kids[1]); } default: { Symbol q; if (p->kids[0] && (q = localaddr(p->kids[0])) != NULL) return q; return localaddr(p->kids[1]); } } } /* whilestmt - while ( expression ) statement */ static void whilestmt(int lab, Swtch swp, int lev) { Coordinate pt; Tree e; refinc *= 10.0; t = gettok(); expect('('); walk(NULL, 0, 0); pt = src; e = texpr(conditional, ')', FUNC); branch(lab + 1); definelab(lab); statement(lab, swp, lev); definelab(lab + 1); definept(&pt); walk(e, lab, 0); if (findlabel(lab + 2)->ref) definelab(lab + 2); } openarena_0.8.8.orig/code/tools/lcc/src/token.h0000644000175000017500000001662011656310263020125 0ustar smcvsmcv/* xx(symbol, value, prec, op, optree, kind, string) */ yy(0, 0, 0, 0, 0, 0, 0) xx(FLOAT, 1, 0, 0, 0, CHAR, "float") xx(DOUBLE, 2, 0, 0, 0, CHAR, "double") xx(CHAR, 3, 0, 0, 0, CHAR, "char") xx(SHORT, 4, 0, 0, 0, CHAR, "short") xx(INT, 5, 0, 0, 0, CHAR, "int") xx(UNSIGNED, 6, 0, 0, 0, CHAR, "unsigned") xx(POINTER, 7, 0, 0, 0, 0, "pointer") xx(VOID, 8, 0, 0, 0, CHAR, "void") xx(STRUCT, 9, 0, 0, 0, CHAR, "struct") xx(UNION, 10, 0, 0, 0, CHAR, "union") xx(FUNCTION, 11, 0, 0, 0, 0, "function") xx(ARRAY, 12, 0, 0, 0, 0, "array") xx(ENUM, 13, 0, 0, 0, CHAR, "enum") xx(LONG, 14, 0, 0, 0, CHAR, "long") xx(CONST, 15, 0, 0, 0, CHAR, "const") xx(VOLATILE, 16, 0, 0, 0, CHAR, "volatile") yy(0, 17, 0, 0, 0, 0, 0) yy(0, 18, 0, 0, 0, 0, 0) yy(0, 19, 0, 0, 0, 0, 0) yy(0, 20, 0, 0, 0, 0, 0) yy(0, 21, 0, 0, 0, 0, 0) yy(0, 22, 0, 0, 0, 0, 0) yy(0, 23, 0, 0, 0, 0, 0) yy(0, 24, 0, 0, 0, 0, 0) yy(0, 25, 0, 0, 0, 0, 0) yy(0, 26, 0, 0, 0, 0, 0) yy(0, 27, 0, 0, 0, 0, 0) yy(0, 28, 0, 0, 0, 0, "long long") yy(0, 29, 0, 0, 0, 0, 0) yy(0, 30, 0, 0, 0, 0, 0) yy(0, 31, 0, 0, 0, 0, "const volatile") xx(ID, 32, 0, 0, 0, ID, "identifier") yy(0, 33, 0, 0, 0, ID, "!") xx(FCON, 34, 0, 0, 0, ID, "floating constant") xx(ICON, 35, 0, 0, 0, ID, "integer constant") xx(SCON, 36, 0, 0, 0, ID, "string constant") yy(0, 37, 13, MOD, bittree,'%', "%") yy(0, 38, 8, BAND, bittree,ID, "&") xx(INCR, 39, 0, ADD, addtree,ID, "++") yy(0, 40, 0, 0, 0, ID, "(") yy(0, 41, 0, 0, 0, ')', ")") yy(0, 42, 13, MUL, multree,ID, "*") yy(0, 43, 12, ADD, addtree,ID, "+") yy(0, 44, 1, 0, 0, ',', ",") yy(0, 45, 12, SUB, subtree,ID, "-") yy(0, 46, 0, 0, 0, '.', ".") yy(0, 47, 13, DIV, multree,'/', "/") xx(DECR, 48, 0, SUB, subtree,ID, "--") xx(DEREF, 49, 0, 0, 0, DEREF, "->") xx(ANDAND, 50, 5, AND, andtree,ANDAND, "&&") xx(OROR, 51, 4, OR, andtree,OROR, "||") xx(LEQ, 52, 10, LE, cmptree,LEQ, "<=") xx(EQL, 53, 9, EQ, eqtree, EQL, "==") xx(NEQ, 54, 9, NE, eqtree, NEQ, "!=") xx(GEQ, 55, 10, GE, cmptree,GEQ, ">=") xx(RSHIFT, 56, 11, RSH, shtree, RSHIFT, ">>") xx(LSHIFT, 57, 11, LSH, shtree, LSHIFT, "<<") yy(0, 58, 0, 0, 0, ':', ":") yy(0, 59, 0, 0, 0, IF, ";") yy(0, 60, 10, LT, cmptree,'<', "<") yy(0, 61, 2, ASGN, asgntree,'=', "=") yy(0, 62, 10, GT, cmptree,'>', ">") yy(0, 63, 0, 0, 0, '?', "?") xx(ELLIPSIS, 64, 0, 0, 0, ELLIPSIS,"...") xx(SIZEOF, 65, 0, 0, 0, ID, "sizeof") yy(0, 66, 0, 0, 0, 0, 0) xx(AUTO, 67, 0, 0, 0, STATIC, "auto") xx(BREAK, 68, 0, 0, 0, IF, "break") xx(CASE, 69, 0, 0, 0, IF, "case") xx(CONTINUE, 70, 0, 0, 0, IF, "continue") xx(DEFAULT, 71, 0, 0, 0, IF, "default") xx(DO, 72, 0, 0, 0, IF, "do") xx(ELSE, 73, 0, 0, 0, IF, "else") xx(EXTERN, 74, 0, 0, 0, STATIC, "extern") xx(FOR, 75, 0, 0, 0, IF, "for") xx(GOTO, 76, 0, 0, 0, IF, "goto") xx(IF, 77, 0, 0, 0, IF, "if") xx(REGISTER, 78, 0, 0, 0, STATIC, "register") xx(RETURN, 79, 0, 0, 0, IF, "return") xx(SIGNED, 80, 0, 0, 0, CHAR, "signed") xx(STATIC, 81, 0, 0, 0, STATIC, "static") xx(SWITCH, 82, 0, 0, 0, IF, "switch") xx(TYPEDEF, 83, 0, 0, 0, STATIC, "typedef") xx(WHILE, 84, 0, 0, 0, IF, "while") xx(TYPECODE, 85, 0, 0, 0, ID, "__typecode") xx(FIRSTARG, 86, 0, 0, 0, ID, "__firstarg") yy(0, 87, 0, 0, 0, 0, 0) yy(0, 88, 0, 0, 0, 0, 0) yy(0, 89, 0, 0, 0, 0, 0) yy(0, 90, 0, 0, 0, 0, 0) yy(0, 91, 0, 0, 0, '[', "[") yy(0, 92, 0, 0, 0, 0, 0) yy(0, 93, 0, 0, 0, ']', "]") yy(0, 94, 7, BXOR, bittree,'^', "^") yy(0, 95, 0, 0, 0, 0, 0) yy(0, 96, 0, 0, 0, 0, 0) yy(0, 97, 0, 0, 0, 0, 0) yy(0, 98, 0, 0, 0, 0, 0) yy(0, 99, 0, 0, 0, 0, 0) yy(0, 100, 0, 0, 0, 0, 0) yy(0, 101, 0, 0, 0, 0, 0) yy(0, 102, 0, 0, 0, 0, 0) yy(0, 103, 0, 0, 0, 0, 0) yy(0, 104, 0, 0, 0, 0, 0) yy(0, 105, 0, 0, 0, 0, 0) yy(0, 106, 0, 0, 0, 0, 0) yy(0, 107, 0, 0, 0, 0, 0) yy(0, 108, 0, 0, 0, 0, 0) yy(0, 109, 0, 0, 0, 0, 0) yy(0, 110, 0, 0, 0, 0, 0) yy(0, 111, 0, 0, 0, 0, 0) yy(0, 112, 0, 0, 0, 0, 0) yy(0, 113, 0, 0, 0, 0, 0) yy(0, 114, 0, 0, 0, 0, 0) yy(0, 115, 0, 0, 0, 0, 0) yy(0, 116, 0, 0, 0, 0, 0) yy(0, 117, 0, 0, 0, 0, 0) yy(0, 118, 0, 0, 0, 0, 0) yy(0, 119, 0, 0, 0, 0, 0) yy(0, 120, 0, 0, 0, 0, 0) yy(0, 121, 0, 0, 0, 0, 0) yy(0, 122, 0, 0, 0, 0, 0) yy(0, 123, 0, 0, 0, IF, "{") yy(0, 124, 6, BOR, bittree,'|', "|") yy(0, 125, 0, 0, 0, '}', "}") yy(0, 126, 0, BCOM, 0, ID, "~") xx(EOI, 127, 0, 0, 0, EOI, "end of input") #undef xx #undef yy openarena_0.8.8.orig/code/tools/lcc/src/trace.c0000644000175000017500000001100011656310262020060 0ustar smcvsmcv#include "c.h" static char *fmt, *fp, *fmtend; /* format string, current & limit pointer */ static Tree args; /* printf arguments */ static Symbol frameno; /* local holding frame number */ /* appendstr - append str to the evolving format string, expanding it if necessary */ static void appendstr(char *str) { do if (fp == fmtend) { if (fp) { char *s = allocate(2*(fmtend - fmt), FUNC); strncpy(s, fmt, fmtend - fmt); fp = s + (fmtend - fmt); fmtend = s + 2*(fmtend - fmt); fmt = s; } else { fp = fmt = allocate(80, FUNC); fmtend = fmt + 80; } } while ((*fp++ = *str++) != 0); fp--; } /* tracevalue - append format and argument to print the value of e */ static void tracevalue(Tree e, int lev) { Type ty = unqual(e->type); switch (ty->op) { case INT: if (ty == chartype || ty == signedchar) appendstr("'\\x%02x'"); else if (ty == longtype) appendstr("0x%ld"); else appendstr("0x%d"); break; case UNSIGNED: if (ty == chartype || ty == unsignedchar) appendstr("'\\x%02x'"); else if (ty == unsignedlong) appendstr("0x%lx"); else appendstr("0x%x"); break; case FLOAT: if (ty == longdouble) appendstr("%Lg"); else appendstr("%g"); break; case POINTER: if (unqual(ty->type) == chartype || unqual(ty->type) == signedchar || unqual(ty->type) == unsignedchar) { static Symbol null; if (null == NULL) null = mkstr("(null)"); tracevalue(cast(e, unsignedtype), lev + 1); appendstr(" \"%.30s\""); e = condtree(e, e, pointer(idtree(null->u.c.loc))); } else { appendstr("("); appendstr(typestring(ty, "")); appendstr(")0x%x"); } break; case STRUCT: { Field q; appendstr("("); appendstr(typestring(ty, "")); appendstr("){"); for (q = ty->u.sym->u.s.flist; q; q = q->link) { appendstr(q->name); appendstr("="); tracevalue(field(addrof(e), q->name), lev + 1); if (q->link) appendstr(","); } appendstr("}"); return; } case UNION: appendstr("("); appendstr(typestring(ty, "")); appendstr("){...}"); return; case ARRAY: if (lev && ty->type->size > 0) { int i; e = pointer(e); appendstr("{"); for (i = 0; i < ty->size/ty->type->size; i++) { Tree p = (*optree['+'])(ADD, e, consttree(i, inttype)); if (isptr(p->type) && isarray(p->type->type)) p = retype(p, p->type->type); else p = rvalue(p); if (i) appendstr(","); tracevalue(p, lev + 1); } appendstr("}"); } else appendstr(typestring(ty, "")); return; default: assert(0); } e = cast(e, promote(ty)); args = tree(mkop(ARG,e->type), e->type, e, args); } /* tracefinis - complete & generate the trace call to print */ static void tracefinis(Symbol printer) { Tree *ap; Symbol p; *fp = 0; p = mkstr(string(fmt)); for (ap = &args; *ap; ap = &(*ap)->kids[1]) ; *ap = tree(ARG+P, charptype, pointer(idtree(p->u.c.loc)), 0); walk(calltree(pointer(idtree(printer)), freturn(printer->type), args, NULL), 0, 0); args = 0; fp = fmtend = 0; } /* tracecall - generate code to trace entry to f */ static void tracecall(Symbol printer, Symbol f) { int i; Symbol counter = genident(STATIC, inttype, GLOBAL); defglobal(counter, BSS); (*IR->space)(counter->type->size); frameno = genident(AUTO, inttype, level); addlocal(frameno); appendstr(f->name); appendstr("#"); tracevalue(asgn(frameno, incr(INCR, idtree(counter), consttree(1, inttype))), 0); appendstr("("); for (i = 0; f->u.f.callee[i]; i++) { if (i) appendstr(","); appendstr(f->u.f.callee[i]->name); appendstr("="); tracevalue(idtree(f->u.f.callee[i]), 0); } if (variadic(f->type)) appendstr(",..."); appendstr(") called\n"); tracefinis(printer); } /* tracereturn - generate code to trace return e */ static void tracereturn(Symbol printer, Symbol f, Tree e) { appendstr(f->name); appendstr("#"); tracevalue(idtree(frameno), 0); appendstr(" returned"); if (freturn(f->type) != voidtype && e) { appendstr(" "); tracevalue(e, 0); } appendstr("\n"); tracefinis(printer); } /* trace_init - initialize for tracing */ void trace_init(int argc, char *argv[]) { int i; static int inited; if (inited) return; inited = 1; type_init(argc, argv); if (IR) for (i = 1; i < argc; i++) if (strncmp(argv[i], "-t", 2) == 0 && strchr(argv[i], '=') == NULL) { Symbol printer = mksymbol(EXTERN, argv[i][2] ? &argv[i][2] : "printf", ftype(inttype, ptr(qual(CONST, chartype)))); printer->defined = 0; attach((Apply)tracecall, printer, &events.entry); attach((Apply)tracereturn, printer, &events.returns); break; } } openarena_0.8.8.orig/code/tools/lcc/src/bind.c0000644000175000017500000000027511656310263017713 0ustar smcvsmcv#include "c.h" extern Interface nullIR; extern Interface bytecodeIR; Binding bindings[] = { { "null", &nullIR }, { "bytecode", &bytecodeIR }, { NULL, NULL }, }; openarena_0.8.8.orig/code/tools/lcc/src/c.h0000644000175000017500000004053711656310263017233 0ustar smcvsmcv#include #include #include #include #include #include #define NEW(p,a) ((p) = allocate(sizeof *(p), (a))) #define NEW0(p,a) memset(NEW((p),(a)), 0, sizeof *(p)) #define isaddrop(op) (specific(op)==ADDRG+P || specific(op)==ADDRL+P \ || specific(op)==ADDRF+P) #define MAXLINE 512 #define BUFSIZE 4096 #define istypename(t,tsym) (kind[t] == CHAR \ || (t == ID && tsym && tsym->sclass == TYPEDEF)) #define sizeop(n) ((n)<<10) #define generic(op) ((op)&0x3F0) #define specific(op) ((op)&0x3FF) #define opindex(op) (((op)>>4)&0x3F) #define opkind(op) ((op)&~0x3F0) #define opsize(op) ((op)>>10) #define optype(op) ((op)&0xF) #ifdef __LCC__ #ifndef __STDC__ #define __STDC__ #endif #endif #define NELEMS(a) ((int)(sizeof (a)/sizeof ((a)[0]))) #undef roundup #define roundup(x,n) (((x)+((n)-1))&(~((n)-1))) #define mkop(op,ty) (specific((op) + ttob(ty))) #define extend(x,ty) ((x)&(1<<(8*(ty)->size-1)) ? (x)|((~0UL)<<(8*(ty)->size-1)) : (x)&ones(8*(ty)->size)) #define ones(n) ((n)>=8*sizeof (unsigned long) ? ~0UL : ~((~0UL)<<(n))) #define isqual(t) ((t)->op >= CONST) #define unqual(t) (isqual(t) ? (t)->type : (t)) #define isvolatile(t) ((t)->op == VOLATILE \ || (t)->op == CONST+VOLATILE) #define isconst(t) ((t)->op == CONST \ || (t)->op == CONST+VOLATILE) #define isarray(t) (unqual(t)->op == ARRAY) #define isstruct(t) (unqual(t)->op == STRUCT \ || unqual(t)->op == UNION) #define isunion(t) (unqual(t)->op == UNION) #define isfunc(t) (unqual(t)->op == FUNCTION) #define isptr(t) (unqual(t)->op == POINTER) #define ischar(t) ((t)->size == 1 && isint(t)) #define isint(t) (unqual(t)->op == INT \ || unqual(t)->op == UNSIGNED) #define isfloat(t) (unqual(t)->op == FLOAT) #define isarith(t) (unqual(t)->op <= UNSIGNED) #define isunsigned(t) (unqual(t)->op == UNSIGNED) #define isscalar(t) (unqual(t)->op <= POINTER \ || unqual(t)->op == ENUM) #define isenum(t) (unqual(t)->op == ENUM) #define fieldsize(p) (p)->bitsize #define fieldright(p) ((p)->lsb - 1) #define fieldleft(p) (8*(p)->type->size - \ fieldsize(p) - fieldright(p)) #define fieldmask(p) (~(~(unsigned)0< #include #include "c.h" #define I(f) s_##f static Node *tail; static int off, maxoff, uid = 0, verbose = 0, html = 0; static const char *yyBEGIN(const char *tag) { if (html) print("<%s>", tag); return tag; } static void yyEND(const char *tag) { if (html) print("", tag); if (isupper(*tag)) print("\n"); } #define BEGIN(tag) do { const char *yytag=yyBEGIN(#tag); #define END yyEND(yytag); } while (0) #define ITEM BEGIN(li) #define START BEGIN(LI) #define ANCHOR(attr,code) do { const char *yytag="a"; if (html) { printf(""); } #define NEWLINE print(html ? "
    \n" : "\n") static void emitCoord(Coordinate src) { if (src.file && *src.file) { ANCHOR(href,print("%s", src.file)); print("%s", src.file); END; print(":"); } print("%d.%d", src.y, src.x); } static void emitString(int len, const char *s) { for ( ; len-- > 0; s++) if (*s == '&' && html) print("&"); else if (*s == '<' && html) print("<"); else if (*s == '>' && html) print("<"); else if (*s == '"' || *s == '\\') print("\\%c", *s); else if (*s >= ' ' && *s < 0177) print("%c", *s); else print("\\%d%d%d", (*s>>6)&3, (*s>>3)&7, *s&7); } static void emitSymRef(Symbol p) { (*IR->defsymbol)(p); ANCHOR(href,print("#%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END; } static void emitSymbol(Symbol p) { (*IR->defsymbol)(p); ANCHOR(name,print("%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END; BEGIN(ul); #define xx(field,code) ITEM; if (!html) print(" "); print(#field "="); code; END if (verbose && (src.y || src.x)) xx(src,emitCoord(p->src)); xx(type,print("%t", p->type)); xx(sclass,print("%k", p->sclass)); switch (p->scope) { case CONSTANTS: xx(scope,print("CONSTANTS")); break; case LABELS: xx(scope,print("LABELS")); break; case GLOBAL: xx(scope,print("GLOBAL")); break; case PARAM: xx(scope,print("PARAM")); break; case LOCAL: xx(scope,print("LOCAL")); break; default: if (p->scope > LOCAL) xx(scope,print("LOCAL+%d", p->scope-LOCAL)); else xx(scope,print("%d", p->scope)); } if (p->scope >= PARAM && p->sclass != STATIC) xx(offset,print("%d", p->x.offset)); xx(ref,print("%f", p->ref)); if (p->temporary && p->u.t.cse) xx(u.t.cse,print("%p", p->u.t.cse)); END; #undef xx } /* address - initialize q for addressing expression p+n */ static void I(address)(Symbol q, Symbol p, long n) { q->name = stringf("%s%s%D", p->name, n > 0 ? "+" : "", n); (*IR->defsymbol)(q); START; print("address "); emitSymbol(q); END; } /* blockbeg - start a block */ static void I(blockbeg)(Env *e) { e->offset = off; START; print("blockbeg off=%d", off); END; } /* blockend - start a block */ static void I(blockend)(Env *e) { if (off > maxoff) maxoff = off; START; print("blockend off=%d", off); END; off = e->offset; } /* defaddress - initialize an address */ static void I(defaddress)(Symbol p){ START; print("defaddress "); emitSymRef(p); END; } /* defconst - define a constant */ static void I(defconst)(int suffix, int size, Value v) { START; print("defconst "); switch (suffix) { case I: print("int.%d ", size); BEGIN(code); if (size > sizeof (int)) print("%D", v.i); else print("%d", (int)v.i); END; break; case U: print("unsigned.%d ", size); BEGIN(code); if (size > sizeof (unsigned)) print("%U", v.u); else print("%u", (unsigned)v.u); END; break; case P: print("void*.%d ", size); BEGIN(code); print("%p", v.p); END; break; case F: print("float.%d ", size); BEGIN(code); print("%g", (double)v.d); END; break; default: assert(0); } END; } /* defstring - emit a string constant */ static void I(defstring)(int len, char *s) { START; print("defstring "); BEGIN(code); print("\""); emitString(len, s); print("\""); END; END; } /* defsymbol - define a symbol: initialize p->x */ static void I(defsymbol)(Symbol p) { if (p->x.name == NULL) p->x.name = stringd(++uid); } /* emit - emit the dags on list p */ static void I(emit)(Node p){ ITEM; if (!html) print(" "); for (; p; p = p->x.next) { if (p->op == LABEL+V) { assert(p->syms[0]); ANCHOR(name,print("%s", p->syms[0]->x.name)); BEGIN(code); print("%s", p->syms[0]->name); END; END; print(":"); } else { int i; if (p->x.listed) { BEGIN(strong); print("%d", p->x.inst); END; print("'"); print(" %s", opname(p->op)); } else print("%d. %s", p->x.inst, opname(p->op)); if (p->count > 1) print(" count=%d", p->count); for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++) print(" #%d", p->kids[i]->x.inst); if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type) print(" {%t}", p->syms[0]->type); else for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++) { print(" "); if (p->syms[i]->scope == CONSTANTS) print(p->syms[i]->name); else emitSymRef(p->syms[i]); } } NEWLINE; } END; } /* export - announce p as exported */ static void I(export)(Symbol p) { START; print("export "); emitSymRef(p); END; } /* function - generate code for a function */ static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) { int i; (*IR->defsymbol)(f); off = 0; for (i = 0; caller[i] && callee[i]; i++) { off = roundup(off, caller[i]->type->align); caller[i]->x.offset = callee[i]->x.offset = off; off += caller[i]->type->size; } if (!html) { print("function "); emitSymbol(f); print(" ncalls=%d\n", ncalls); for (i = 0; caller[i]; i++) START; print("caller "); emitSymbol(caller[i]); END; for (i = 0; callee[i]; i++) START; print("callee "); emitSymbol(callee[i]); END; } else { START; print("function"); BEGIN(UL); #define xx(field,code) ITEM; print(#field "="); code; END xx(f,emitSymbol(f)); xx(ncalls,print("%d", ncalls)); if (caller[0]) { ITEM; print("caller"); BEGIN(OL); for (i = 0; caller[i]; i++) ITEM; emitSymbol(caller[i]); END; END; END; ITEM; print("callee"); BEGIN(OL); for (i = 0; callee[i]; i++) ITEM; emitSymbol(callee[i]); END; END; END; } else { xx(caller,BEGIN(em); print("empty"); END); xx(callee,BEGIN(em); print("empty"); END); } END; END; } maxoff = off = 0; gencode(caller, callee); if (html) START; print("emitcode"); BEGIN(ul); emitcode(); END; END; else emitcode(); START; print("maxoff=%d", maxoff); END; #undef xx } /* visit - generate code for *p */ static int visit(Node p, int n) { if (p && p->x.inst == 0) { p->x.inst = ++n; n = visit(p->kids[0], n); n = visit(p->kids[1], n); *tail = p; tail = &p->x.next; } return n; } /* gen0 - generate code for the dags on list p */ static Node I(gen)(Node p) { int n; Node nodelist; tail = &nodelist; for (n = 0; p; p = p->link) { switch (generic(p->op)) { /* check for valid forest */ case CALL: assert(IR->wants_dag || p->count == 0); break; case ARG: case ASGN: case JUMP: case LABEL: case RET: case EQ: case GE: case GT: case LE: case LT: case NE: assert(p->count == 0); break; case INDIR: assert(IR->wants_dag && p->count > 0); break; default: assert(0); } check(p); p->x.listed = 1; n = visit(p, n); } *tail = 0; return nodelist; } /* global - announce a global */ static void I(global)(Symbol p) { START; print("global "); emitSymbol(p); END; } /* import - import a symbol */ static void I(import)(Symbol p) { START; print("import "); emitSymRef(p); END; } /* local - local variable */ static void I(local)(Symbol p) { if (p->temporary) p->name = stringf("t%s", p->name); (*IR->defsymbol)(p); off = roundup(off, p->type->align); p->x.offset = off; off += p->type->size; START; print(p->temporary ? "temporary " : "local "); emitSymbol(p); END; } /* progbeg - beginning of program */ static void I(progbeg)(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) if (strcmp(argv[i], "-v") == 0) verbose++; else if (strcmp(argv[i], "-html") == 0) html++; if (html) { print("\n"); print(""); BEGIN(head); if (firstfile && *firstfile) BEGIN(title); emitString(strlen(firstfile), firstfile); END; print("\n"); END; print("\n"); if (firstfile && *firstfile) BEGIN(h1); emitString(strlen(firstfile), firstfile); END; BEGIN(P); BEGIN(em); print("Links lead from uses of identifiers and labels to their definitions."); END; END; print("
      \n"); START; print("progbeg"); BEGIN(ol); for (i = 1; i < argc; i++) { ITEM; BEGIN(code); print("\""); emitString(strlen(argv[i]), argv[i]); print("\""); END; END; } END; END; } } /* progend - end of program */ static void I(progend)(void) { START; print("progend"); END; if (html) { time_t t; print("
    \n"); time(&t); print("
    %s
    \n", ctime(&t)); print("\n"); } } /* segment - switch to segment s */ static void I(segment)(int s) { START; print("segment %s", &"text\0bss\0.data\0lit\0.sym\0."[5*s-5]); END; } /* space - initialize n bytes of space */ static void I(space)(int n) { START; print("space %d", n); END; } static void I(stabblock)(int brace, int lev, Symbol *p) {} /* stabend - finalize stab output */ static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) { int i; if (p) emitSymRef(p); print("\n"); if (cpp && sp) for (i = 0; cpp[i] && sp[i]; i++) { print("%w.%d: ", cpp[i], cpp[i]->x); emitSymRef(sp[i]); print("\n"); } } static void I(stabfend)(Symbol p, int lineno) {} static void I(stabinit)(char *file, int argc, char *argv[]) {} /* stabline - emit line number information for source coordinate *cp */ static void I(stabline)(Coordinate *cp) { if (cp->file) print("%s:", cp->file); print("%d.%d:\n", cp->y, cp->x); } static void I(stabsym)(Symbol p) {} static void I(stabtype)(Symbol p) {} Interface symbolicIR = { {1, 1, 0}, /* char */ {2, 2, 0}, /* short */ {4, 4, 0}, /* int */ {4, 4, 0}, /* long */ {4, 4, 0}, /* long long */ {4, 4, 1}, /* float */ {8, 8, 1}, /* double */ {8, 8, 1}, /* long double */ {4, 4, 0}, /* T* */ {0, 4, 0}, /* struct */ 0, /* little_endian */ 0, /* mulops_calls */ 0, /* wants_callb */ 1, /* wants_argb */ 1, /* left_to_right */ 1, /* wants_dag */ 0, /* unsigned_char */ I(address), I(blockbeg), I(blockend), I(defaddress), I(defconst), I(defstring), I(defsymbol), I(emit), I(export), I(function), I(gen), I(global), I(import), I(local), I(progbeg), I(progend), I(segment), I(space), I(stabblock), I(stabend), I(stabfend), I(stabinit), I(stabline), I(stabsym), I(stabtype) }; Interface symbolic64IR = { {1, 1, 0}, /* char */ {2, 2, 0}, /* short */ {4, 4, 0}, /* int */ {8, 8, 0}, /* long */ {8, 8, 0}, /* long long */ {4, 4, 1}, /* float */ {8, 8, 1}, /* double */ {8, 8, 1}, /* long double */ {8, 8, 0}, /* T* */ {0, 1, 0}, /* struct */ 1, /* little_endian */ 0, /* mulops_calls */ 0, /* wants_callb */ 1, /* wants_argb */ 1, /* left_to_right */ 1, /* wants_dag */ 0, /* unsigned_char */ I(address), I(blockbeg), I(blockend), I(defaddress), I(defconst), I(defstring), I(defsymbol), I(emit), I(export), I(function), I(gen), I(global), I(import), I(local), I(progbeg), I(progend), I(segment), I(space), I(stabblock), I(stabend), I(stabfend), I(stabinit), I(stabline), I(stabsym), I(stabtype) }; openarena_0.8.8.orig/code/tools/lcc/src/types.c0000644000175000017500000005057311656310262020150 0ustar smcvsmcv#include "c.h" #include static Field isfield(const char *, Field); static Type type(int, Type, int, int, void *); static struct entry { struct type type; struct entry *link; } *typetable[128]; static int maxlevel; static Symbol pointersym; Type chartype; /* char */ Type doubletype; /* double */ Type floattype; /* float */ Type inttype; /* signed int */ Type longdouble; /* long double */ Type longtype; /* long */ Type longlong; /* long long */ Type shorttype; /* signed short int */ Type signedchar; /* signed char */ Type unsignedchar; /* unsigned char */ Type unsignedlong; /* unsigned long int */ Type unsignedlonglong; /* unsigned long long int */ Type unsignedshort; /* unsigned short int */ Type unsignedtype; /* unsigned int */ Type funcptype; /* void (*)() */ Type charptype; /* char* */ Type voidptype; /* void* */ Type voidtype; /* basic types: void */ Type unsignedptr; /* unsigned type to hold void* */ Type signedptr; /* signed type to hold void* */ Type widechar; /* unsigned type that represents wchar_t */ static Type xxinit(int op, char *name, Metrics m) { Symbol p = install(string(name), &types, GLOBAL, PERM); Type ty = type(op, 0, m.size, m.align, p); assert(ty->align == 0 || ty->size%ty->align == 0); p->type = ty; p->addressed = m.outofline; switch (ty->op) { case INT: p->u.limits.max.i = ones(8*ty->size)>>1; p->u.limits.min.i = -p->u.limits.max.i - 1; break; case UNSIGNED: p->u.limits.max.u = ones(8*ty->size); p->u.limits.min.u = 0; break; case FLOAT: if (ty->size == sizeof (float)) p->u.limits.max.d = FLT_MAX; else if (ty->size == sizeof (double)) p->u.limits.max.d = DBL_MAX; else p->u.limits.max.d = LDBL_MAX; p->u.limits.min.d = -p->u.limits.max.d; break; default: assert(0); } return ty; } static Type type(int op, Type ty, int size, int align, void *sym) { unsigned h = (op^((unsigned long)ty>>3)) &(NELEMS(typetable)-1); struct entry *tn; if (op != FUNCTION && (op != ARRAY || size > 0)) for (tn = typetable[h]; tn; tn = tn->link) if (tn->type.op == op && tn->type.type == ty && tn->type.size == size && tn->type.align == align && tn->type.u.sym == sym) return &tn->type; NEW0(tn, PERM); tn->type.op = op; tn->type.type = ty; tn->type.size = size; tn->type.align = align; tn->type.u.sym = sym; tn->link = typetable[h]; typetable[h] = tn; return &tn->type; } void type_init(int argc, char *argv[]) { static int inited; int i; if (inited) return; inited = 1; if (!IR) return; for (i = 1; i < argc; i++) { int size, align, outofline; if (strncmp(argv[i], "-unsigned_char=", 15) == 0) IR->unsigned_char = argv[i][15] - '0'; #define xx(name) \ else if (sscanf(argv[i], "-" #name "=%d,%d,%d", &size, &align, &outofline) == 3) { \ IR->name.size = size; IR->name.align = align; \ IR->name.outofline = outofline; } xx(charmetric) xx(shortmetric) xx(intmetric) xx(longmetric) xx(longlongmetric) xx(floatmetric) xx(doublemetric) xx(longdoublemetric) xx(ptrmetric) xx(structmetric) #undef xx } #define xx(v,name,op,metrics) v=xxinit(op,name,IR->metrics) xx(chartype, "char", IR->unsigned_char ? UNSIGNED : INT,charmetric); xx(doubletype, "double", FLOAT, doublemetric); xx(floattype, "float", FLOAT, floatmetric); xx(inttype, "int", INT, intmetric); xx(longdouble, "long double", FLOAT, longdoublemetric); xx(longtype, "long int", INT, longmetric); xx(longlong, "long long int", INT, longlongmetric); xx(shorttype, "short", INT, shortmetric); xx(signedchar, "signed char", INT, charmetric); xx(unsignedchar, "unsigned char", UNSIGNED,charmetric); xx(unsignedlong, "unsigned long", UNSIGNED,longmetric); xx(unsignedshort, "unsigned short", UNSIGNED,shortmetric); xx(unsignedtype, "unsigned int", UNSIGNED,intmetric); xx(unsignedlonglong,"unsigned long long",UNSIGNED,longlongmetric); #undef xx { Symbol p; p = install(string("void"), &types, GLOBAL, PERM); voidtype = type(VOID, NULL, 0, 0, p); p->type = voidtype; } pointersym = install(string("T*"), &types, GLOBAL, PERM); pointersym->addressed = IR->ptrmetric.outofline; pointersym->u.limits.max.p = (void*)ones(8*IR->ptrmetric.size); pointersym->u.limits.min.p = 0; voidptype = ptr(voidtype); funcptype = ptr(func(voidtype, NULL, 1)); charptype = ptr(chartype); #define xx(v,t) if (v==NULL && t->size==voidptype->size && t->align==voidptype->align) v=t xx(unsignedptr,unsignedshort); xx(unsignedptr,unsignedtype); xx(unsignedptr,unsignedlong); xx(unsignedptr,unsignedlonglong); if (unsignedptr == NULL) unsignedptr = type(UNSIGNED, NULL, voidptype->size, voidptype->align, voidptype->u.sym); xx(signedptr,shorttype); xx(signedptr,inttype); xx(signedptr,longtype); xx(signedptr,longlong); if (signedptr == NULL) signedptr = type(INT, NULL, voidptype->size, voidptype->align, voidptype->u.sym); #undef xx widechar = unsignedshort; for (i = 0; i < argc; i++) { #define xx(name,type) \ if (strcmp(argv[i], "-wchar_t=" #name) == 0) \ widechar = type; xx(unsigned_char,unsignedchar) xx(unsigned_int,unsignedtype) xx(unsigned_short,unsignedshort) } #undef xx } void rmtypes(int lev) { if (maxlevel >= lev) { int i; maxlevel = 0; for (i = 0; i < NELEMS(typetable); i++) { struct entry *tn, **tq = &typetable[i]; while ((tn = *tq) != NULL) if (tn->type.op == FUNCTION) tq = &tn->link; else if (tn->type.u.sym && tn->type.u.sym->scope >= lev) *tq = tn->link; else { if (tn->type.u.sym && tn->type.u.sym->scope > maxlevel) maxlevel = tn->type.u.sym->scope; tq = &tn->link; } } } } Type ptr(Type ty) { return type(POINTER, ty, IR->ptrmetric.size, IR->ptrmetric.align, pointersym); } Type deref(Type ty) { if (isptr(ty)) ty = ty->type; else error("type error: %s\n", "pointer expected"); return isenum(ty) ? unqual(ty)->type : ty; } Type array(Type ty, int n, int a) { assert(ty); if (isfunc(ty)) { error("illegal type `array of %t'\n", ty); return array(inttype, n, 0); } if (isarray(ty) && ty->size == 0) error("missing array size\n"); if (ty->size == 0) { if (unqual(ty) == voidtype) error("illegal type `array of %t'\n", ty); else if (Aflag >= 2) warning("declaring type array of %t' is undefined\n", ty); } else if (n > INT_MAX/ty->size) { error("size of `array of %t' exceeds %d bytes\n", ty, INT_MAX); n = 1; } return type(ARRAY, ty, n*ty->size, a ? a : ty->align, NULL); } Type atop(Type ty) { if (isarray(ty)) return ptr(ty->type); error("type error: %s\n", "array expected"); return ptr(ty); } Type qual(int op, Type ty) { if (isarray(ty)) ty = type(ARRAY, qual(op, ty->type), ty->size, ty->align, NULL); else if (isfunc(ty)) warning("qualified function type ignored\n"); else if ((isconst(ty) && op == CONST) || (isvolatile(ty) && op == VOLATILE)) error("illegal type `%k %t'\n", op, ty); else { if (isqual(ty)) { op += ty->op; ty = ty->type; } ty = type(op, ty, ty->size, ty->align, NULL); } return ty; } Type func(Type ty, Type *proto, int style) { if (ty && (isarray(ty) || isfunc(ty))) error("illegal return type `%t'\n", ty); ty = type(FUNCTION, ty, 0, 0, NULL); ty->u.f.proto = proto; ty->u.f.oldstyle = style; return ty; } Type freturn(Type ty) { if (isfunc(ty)) return ty->type; error("type error: %s\n", "function expected"); return inttype; } int variadic(Type ty) { if (isfunc(ty) && ty->u.f.proto) { int i; for (i = 0; ty->u.f.proto[i]; i++) ; return i > 1 && ty->u.f.proto[i-1] == voidtype; } return 0; } Type newstruct(int op, char *tag) { Symbol p; assert(tag); if (*tag == 0) tag = stringd(genlabel(1)); else if ((p = lookup(tag, types)) != NULL && (p->scope == level || (p->scope == PARAM && level == PARAM+1))) { if (p->type->op == op && !p->defined) return p->type; error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); } p = install(tag, &types, level, PERM); p->type = type(op, NULL, 0, 0, p); if (p->scope > maxlevel) maxlevel = p->scope; p->src = src; return p->type; } Field newfield(char *name, Type ty, Type fty) { Field p, *q = &ty->u.sym->u.s.flist; if (name == NULL) name = stringd(genlabel(1)); for (p = *q; p; q = &p->link, p = *q) if (p->name == name) error("duplicate field name `%s' in `%t'\n", name, ty); NEW0(p, PERM); *q = p; p->name = name; p->type = fty; if (xref) { /* omit */ if (ty->u.sym->u.s.ftab == NULL) /* omit */ ty->u.sym->u.s.ftab = table(NULL, level); /* omit */ install(name, &ty->u.sym->u.s.ftab, 0, PERM)->src = src;/* omit */ } /* omit */ return p; } int eqtype(Type ty1, Type ty2, int ret) { if (ty1 == ty2) return 1; if (ty1->op != ty2->op) return 0; switch (ty1->op) { case ENUM: case UNION: case STRUCT: case UNSIGNED: case INT: case FLOAT: return 0; case POINTER: return eqtype(ty1->type, ty2->type, 1); case VOLATILE: case CONST+VOLATILE: case CONST: return eqtype(ty1->type, ty2->type, 1); case ARRAY: if (eqtype(ty1->type, ty2->type, 1)) { if (ty1->size == ty2->size) return 1; if (ty1->size == 0 || ty2->size == 0) return ret; } return 0; case FUNCTION: if (eqtype(ty1->type, ty2->type, 1)) { Type *p1 = ty1->u.f.proto, *p2 = ty2->u.f.proto; if (p1 == p2) return 1; if (p1 && p2) { for ( ; *p1 && *p2; p1++, p2++) if (eqtype(unqual(*p1), unqual(*p2), 1) == 0) return 0; if (*p1 == NULL && *p2 == NULL) return 1; } else { if (variadic(p1 ? ty1 : ty2)) return 0; if (p1 == NULL) p1 = p2; for ( ; *p1; p1++) { Type ty = unqual(*p1); if (promote(ty) != (isenum(ty) ? ty->type : ty)) return 0; } return 1; } } return 0; } assert(0); return 0; } Type promote(Type ty) { ty = unqual(ty); switch (ty->op) { case ENUM: return inttype; case INT: if (ty->size < inttype->size) return inttype; break; case UNSIGNED: if (ty->size < inttype->size) return inttype; if (ty->size < unsignedtype->size) return unsignedtype; break; case FLOAT: if (ty->size < doubletype->size) return doubletype; } return ty; } Type signedint(Type ty) { if (ty->op == INT) return ty; assert(ty->op == UNSIGNED); #define xx(t) if (ty->size == t->size) return t xx(inttype); xx(longtype); xx(longlong); #undef xx assert(0); return NULL; } Type compose(Type ty1, Type ty2) { if (ty1 == ty2) return ty1; assert(ty1->op == ty2->op); switch (ty1->op) { case POINTER: return ptr(compose(ty1->type, ty2->type)); case CONST+VOLATILE: return qual(CONST, qual(VOLATILE, compose(ty1->type, ty2->type))); case CONST: case VOLATILE: return qual(ty1->op, compose(ty1->type, ty2->type)); case ARRAY: { Type ty = compose(ty1->type, ty2->type); if (ty1->size && ((ty1->type->size && ty2->size == 0) || ty1->size == ty2->size)) return array(ty, ty1->size/ty1->type->size, ty1->align); if (ty2->size && ty2->type->size && ty1->size == 0) return array(ty, ty2->size/ty2->type->size, ty2->align); return array(ty, 0, 0); } case FUNCTION: { Type *p1 = ty1->u.f.proto, *p2 = ty2->u.f.proto; Type ty = compose(ty1->type, ty2->type); List tlist = NULL; if (p1 == NULL && p2 == NULL) return func(ty, NULL, 1); if (p1 && p2 == NULL) return func(ty, p1, ty1->u.f.oldstyle); if (p2 && p1 == NULL) return func(ty, p2, ty2->u.f.oldstyle); for ( ; *p1 && *p2; p1++, p2++) { Type ty = compose(unqual(*p1), unqual(*p2)); if (isconst(*p1) || isconst(*p2)) ty = qual(CONST, ty); if (isvolatile(*p1) || isvolatile(*p2)) ty = qual(VOLATILE, ty); tlist = append(ty, tlist); } assert(*p1 == NULL && *p2 == NULL); return func(ty, ltov(&tlist, PERM), 0); } } assert(0); return NULL; } int ttob(Type ty) { switch (ty->op) { case CONST: case VOLATILE: case CONST+VOLATILE: return ttob(ty->type); case VOID: case INT: case UNSIGNED: case FLOAT: return ty->op + sizeop(ty->size); case POINTER: return POINTER + sizeop(voidptype->size); case FUNCTION: return POINTER + sizeop(funcptype->size); case ARRAY: case STRUCT: case UNION: return STRUCT; case ENUM: return INT + sizeop(inttype->size); } assert(0); return INT; } Type btot(int op, int size) { #define xx(ty) if (size == (ty)->size) return ty; switch (optype(op)) { case F: xx(floattype); xx(doubletype); xx(longdouble); assert(0); return 0; case I: if (chartype->op == INT) xx(chartype); xx(signedchar); xx(shorttype); xx(inttype); xx(longtype); xx(longlong); assert(0); return 0; case U: if (chartype->op == UNSIGNED) xx(chartype); xx(unsignedchar); xx(unsignedshort); xx(unsignedtype); xx(unsignedlong); xx(unsignedlonglong); assert(0); return 0; case P: xx(voidptype); xx(funcptype); assert(0); return 0; } #undef xx assert(0); return 0; } int hasproto(Type ty) { if (ty == 0) return 1; switch (ty->op) { case CONST: case VOLATILE: case CONST+VOLATILE: case POINTER: case ARRAY: return hasproto(ty->type); case FUNCTION: return hasproto(ty->type) && ty->u.f.proto; case STRUCT: case UNION: case VOID: case FLOAT: case ENUM: case INT: case UNSIGNED: return 1; } assert(0); return 0; } /* fieldlist - construct a flat list of fields in type ty */ Field fieldlist(Type ty) { return ty->u.sym->u.s.flist; } /* fieldref - find field name of type ty, return entry */ Field fieldref(const char *name, Type ty) { Field p = isfield(name, unqual(ty)->u.sym->u.s.flist); if (p && xref) { Symbol q; assert(unqual(ty)->u.sym->u.s.ftab); q = lookup(name, unqual(ty)->u.sym->u.s.ftab); assert(q); use(q, src); } return p; } /* ftype - return a function type for rty function (ty,...)' */ Type ftype(Type rty, Type ty) { List list = append(ty, NULL); list = append(voidtype, list); return func(rty, ltov(&list, PERM), 0); } /* isfield - if name is a field in flist, return pointer to the field structure */ static Field isfield(const char *name, Field flist) { for ( ; flist; flist = flist->link) if (flist->name == name) break; return flist; } /* outtype - output type ty */ void outtype(Type ty, FILE *f) { switch (ty->op) { case CONST+VOLATILE: case CONST: case VOLATILE: fprint(f, "%k %t", ty->op, ty->type); break; case STRUCT: case UNION: case ENUM: assert(ty->u.sym); if (ty->size == 0) fprint(f, "incomplete "); assert(ty->u.sym->name); if (*ty->u.sym->name >= '1' && *ty->u.sym->name <= '9') { Symbol p = findtype(ty); if (p == 0) fprint(f, "%k defined at %w", ty->op, &ty->u.sym->src); else fprint(f, p->name); } else { fprint(f, "%k %s", ty->op, ty->u.sym->name); if (ty->size == 0) fprint(f, " defined at %w", &ty->u.sym->src); } break; case VOID: case FLOAT: case INT: case UNSIGNED: fprint(f, ty->u.sym->name); break; case POINTER: fprint(f, "pointer to %t", ty->type); break; case FUNCTION: fprint(f, "%t function", ty->type); if (ty->u.f.proto && ty->u.f.proto[0]) { int i; fprint(f, "(%t", ty->u.f.proto[0]); for (i = 1; ty->u.f.proto[i]; i++) if (ty->u.f.proto[i] == voidtype) fprint(f, ",..."); else fprint(f, ",%t", ty->u.f.proto[i]); fprint(f, ")"); } else if (ty->u.f.proto && ty->u.f.proto[0] == 0) fprint(f, "(void)"); break; case ARRAY: if (ty->size > 0 && ty->type && ty->type->size > 0) { fprint(f, "array %d", ty->size/ty->type->size); while (ty->type && isarray(ty->type) && ty->type->type->size > 0) { ty = ty->type; fprint(f, ",%d", ty->size/ty->type->size); } } else fprint(f, "incomplete array"); if (ty->type) fprint(f, " of %t", ty->type); break; default: assert(0); } } /* printdecl - output a C declaration for symbol p of type ty */ void printdecl(Symbol p, Type ty) { switch (p->sclass) { case AUTO: fprint(stderr, "%s;\n", typestring(ty, p->name)); break; case STATIC: case EXTERN: fprint(stderr, "%k %s;\n", p->sclass, typestring(ty, p->name)); break; case TYPEDEF: case ENUM: break; default: assert(0); } } /* printproto - output a prototype declaration for function p */ void printproto(Symbol p, Symbol callee[]) { if (p->type->u.f.proto) printdecl(p, p->type); else { int i; List list = 0; if (callee[0] == 0) list = append(voidtype, list); else for (i = 0; callee[i]; i++) list = append(callee[i]->type, list); printdecl(p, func(freturn(p->type), ltov(&list, PERM), 0)); } } /* prtype - print details of type ty on f with given indent */ static void prtype(Type ty, FILE *f, int indent, unsigned mark) { switch (ty->op) { default: fprint(f, "(%d %d %d [%p])", ty->op, ty->size, ty->align, ty->u.sym); break; case FLOAT: case INT: case UNSIGNED: case VOID: fprint(f, "(%k %d %d [\"%s\"])", ty->op, ty->size, ty->align, ty->u.sym->name); break; case CONST+VOLATILE: case CONST: case VOLATILE: case POINTER: case ARRAY: fprint(f, "(%k %d %d ", ty->op, ty->size, ty->align); prtype(ty->type, f, indent+1, mark); fprint(f, ")"); break; case STRUCT: case UNION: fprint(f, "(%k %d %d [\"%s\"]", ty->op, ty->size, ty->align, ty->u.sym->name); if (ty->x.marked != mark) { Field p; ty->x.marked = mark; for (p = ty->u.sym->u.s.flist; p; p = p->link) { fprint(f, "\n%I", indent+1); prtype(p->type, f, indent+1, mark); fprint(f, " %s@%d", p->name, p->offset); if (p->lsb) fprint(f, ":%d..%d", fieldsize(p) + fieldright(p), fieldright(p)); } fprint(f, "\n%I", indent); } fprint(f, ")"); break; case ENUM: fprint(f, "(%k %d %d [\"%s\"]", ty->op, ty->size, ty->align, ty->u.sym->name); if (ty->x.marked != mark) { int i; Symbol *p = ty->u.sym->u.idlist; ty->x.marked = mark; for (i = 0; p[i] != NULL; i++) fprint(f, "%I%s=%d\n", indent+1, p[i]->name, p[i]->u.value); } fprint(f, ")"); break; case FUNCTION: fprint(f, "(%k %d %d ", ty->op, ty->size, ty->align); prtype(ty->type, f, indent+1, mark); if (ty->u.f.proto) { int i; fprint(f, "\n%I{", indent+1); for (i = 0; ty->u.f.proto[i]; i++) { if (i > 0) fprint(f, "%I", indent+2); prtype(ty->u.f.proto[i], f, indent+2, mark); fprint(f, "\n"); } fprint(f, "%I}", indent+1); } fprint(f, ")"); break; } } /* printtype - print details of type ty on fd */ void printtype(Type ty, int fd) { static unsigned mark; prtype(ty, fd == 1 ? stdout : stderr, 0, ++mark); fprint(fd == 1 ? stdout : stderr, "\n"); } /* typestring - return ty as C declaration for str, which may be "" */ char *typestring(Type ty, char *str) { for ( ; ty; ty = ty->type) { Symbol p; switch (ty->op) { case CONST+VOLATILE: case CONST: case VOLATILE: if (isptr(ty->type)) str = stringf("%k %s", ty->op, str); else return stringf("%k %s", ty->op, typestring(ty->type, str)); break; case STRUCT: case UNION: case ENUM: assert(ty->u.sym); if ((p = findtype(ty)) != NULL) return *str ? stringf("%s %s", p->name, str) : p->name; if (*ty->u.sym->name >= '1' && *ty->u.sym->name <= '9') warning("unnamed %k in prototype\n", ty->op); if (*str) return stringf("%k %s %s", ty->op, ty->u.sym->name, str); else return stringf("%k %s", ty->op, ty->u.sym->name); case VOID: case FLOAT: case INT: case UNSIGNED: return *str ? stringf("%s %s", ty->u.sym->name, str) : ty->u.sym->name; case POINTER: if (!ischar(ty->type) && (p = findtype(ty)) != NULL) return *str ? stringf("%s %s", p->name, str) : p->name; str = stringf(isarray(ty->type) || isfunc(ty->type) ? "(*%s)" : "*%s", str); break; case FUNCTION: if ((p = findtype(ty)) != NULL) return *str ? stringf("%s %s", p->name, str) : p->name; if (ty->u.f.proto == 0) str = stringf("%s()", str); else if (ty->u.f.proto[0]) { int i; str = stringf("%s(%s", str, typestring(ty->u.f.proto[0], "")); for (i = 1; ty->u.f.proto[i]; i++) if (ty->u.f.proto[i] == voidtype) str = stringf("%s, ...", str); else str = stringf("%s, %s", str, typestring(ty->u.f.proto[i], "")); str = stringf("%s)", str); } else str = stringf("%s(void)", str); break; case ARRAY: if ((p = findtype(ty)) != NULL) return *str ? stringf("%s %s", p->name, str) : p->name; if (ty->type && ty->type->size > 0) str = stringf("%s[%d]", str, ty->size/ty->type->size); else str = stringf("%s[]", str); break; default: assert(0); } } assert(0); return 0; } openarena_0.8.8.orig/code/tools/lcc/src/error.c0000644000175000017500000000545411656310262020133 0ustar smcvsmcv#include "c.h" static void printtoken(void); int errcnt = 0; int errlimit = 20; char kind[] = { #define xx(a,b,c,d,e,f,g) f, #define yy(a,b,c,d,e,f,g) f, #include "token.h" }; int wflag; /* != 0 to suppress warning messages */ void test(int tok, char set[]) { if (t == tok) t = gettok(); else { expect(tok); skipto(tok, set); if (t == tok) t = gettok(); } } void expect(int tok) { if (t == tok) t = gettok(); else { error("syntax error; found"); printtoken(); fprint(stderr, " expecting `%k'\n", tok); } } void error(const char *fmt, ...) { va_list ap; if (errcnt++ >= errlimit) { errcnt = -1; error("too many errors\n"); exit(1); } va_start(ap, fmt); if (firstfile != file && firstfile && *firstfile) fprint(stderr, "%s: ", firstfile); fprint(stderr, "%w: ", &src); vfprint(stderr, NULL, fmt, ap); va_end(ap); } void skipto(int tok, char set[]) { int n; char *s; assert(set); for (n = 0; t != EOI && t != tok; t = gettok()) { for (s = set; *s && kind[t] != *s; s++) ; if (kind[t] == *s) break; if (n++ == 0) error("skipping"); if (n <= 8) printtoken(); else if (n == 9) fprint(stderr, " ..."); } if (n > 8) { fprint(stderr, " up to"); printtoken(); } if (n > 0) fprint(stderr, "\n"); } /* fatal - issue fatal error message and exit */ int fatal(const char *name, const char *fmt, int n) { print("\n"); errcnt = -1; error("compiler error in %s--", name); fprint(stderr, fmt, n); exit(EXIT_FAILURE); return 0; } /* printtoken - print current token preceeded by a space */ static void printtoken(void) { switch (t) { case ID: fprint(stderr, " `%s'", token); break; case ICON: fprint(stderr, " `%s'", vtoa(tsym->type, tsym->u.c.v)); break; case SCON: { int i, n; if (ischar(tsym->type->type)) { char *s = tsym->u.c.v.p; n = tsym->type->size; fprint(stderr, " \""); for (i = 0; i < 20 && i < n && *s; s++, i++) if (*s < ' ' || *s >= 0177) fprint(stderr, "\\%o", *s); else fprint(stderr, "%c", *s); } else { /* wchar_t string */ unsigned int *s = tsym->u.c.v.p; assert(tsym->type->type->size == widechar->size); n = tsym->type->size/widechar->size; fprint(stderr, " L\""); for (i = 0; i < 20 && i < n && *s; s++, i++) if (*s < ' ' || *s >= 0177) fprint(stderr, "\\x%x", *s); else fprint(stderr, "%c", *s); } if (i < n) fprint(stderr, " ..."); else fprint(stderr, "\""); break; } case FCON: fprint(stderr, " `%S'", token, (char*)cp - token); break; case '`': case '\'': fprint(stderr, " \"%k\"", t); break; default: fprint(stderr, " `%k'", t); } } /* warning - issue warning error message */ void warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (wflag == 0) { errcnt--; error("warning: "); vfprint(stderr, NULL, fmt, ap); } va_end(ap); } openarena_0.8.8.orig/code/tools/lcc/src/enode.c0000644000175000017500000003373611656310262020100 0ustar smcvsmcv#include "c.h" static Tree addtree(int, Tree, Tree); static Tree andtree(int, Tree, Tree); static Tree cmptree(int, Tree, Tree); static int compatible(Type, Type); static int isnullptr(Tree e); static Tree multree(int, Tree, Tree); static Tree subtree(int, Tree, Tree); #define isvoidptr(ty) \ (isptr(ty) && unqual(ty->type) == voidtype) Tree (*optree[])(int, Tree, Tree) = { #define xx(a,b,c,d,e,f,g) e, #define yy(a,b,c,d,e,f,g) e, #include "token.h" }; Tree call(Tree f, Type fty, Coordinate src) { int n = 0; Tree args = NULL, r = NULL, e; Type *proto, rty = unqual(freturn(fty)); Symbol t3 = NULL; if (fty->u.f.oldstyle) proto = NULL; else proto = fty->u.f.proto; if (hascall(f)) r = f; if (isstruct(rty)) { t3 = temporary(AUTO, unqual(rty)); if (rty->size == 0) error("illegal use of incomplete type `%t'\n", rty); } if (t != ')') for (;;) { Tree q = pointer(expr1(0)); if (proto && *proto && *proto != voidtype) { Type aty; q = value(q); aty = assign(*proto, q); if (aty) q = cast(q, aty); else error("type error in argument %d to %s; found `%t' expected `%t'\n", n + 1, funcname(f), q->type, *proto); if ((isint(q->type) || isenum(q->type)) && q->type->size != inttype->size) q = cast(q, promote(q->type)); ++proto; } else { if (!fty->u.f.oldstyle && *proto == NULL) error("too many arguments to %s\n", funcname(f)); q = value(q); if (isarray(q->type) || q->type->size == 0) error("type error in argument %d to %s; `%t' is illegal\n", n + 1, funcname(f), q->type); else q = cast(q, promote(q->type)); } if (!IR->wants_argb && isstruct(q->type)) { if (iscallb(q)) q = addrof(q); else { Symbol t1 = temporary(AUTO, unqual(q->type)); q = asgn(t1, q); q = tree(RIGHT, ptr(t1->type), root(q), lvalue(idtree(t1))); } } if (q->type->size == 0) q->type = inttype; if (hascall(q)) r = r ? tree(RIGHT, voidtype, r, q) : q; args = tree(mkop(ARG, q->type), q->type, q, args); n++; if (Aflag >= 2 && n == 32) warning("more than 31 arguments in a call to %s\n", funcname(f)); if (t != ',') break; t = gettok(); } expect(')'); if (proto && *proto && *proto != voidtype) error("insufficient number of arguments to %s\n", funcname(f)); if (r) args = tree(RIGHT, voidtype, r, args); e = calltree(f, rty, args, t3); if (events.calls) apply(events.calls, &src, &e); return e; } Tree calltree(Tree f, Type ty, Tree args, Symbol t3) { Tree p; if (args) f = tree(RIGHT, f->type, args, f); if (isstruct(ty)) assert(t3), p = tree(RIGHT, ty, tree(CALL+B, ty, f, addrof(idtree(t3))), idtree(t3)); else { Type rty = ty; if (isenum(ty)) rty = unqual(ty)->type; if (!isfloat(rty)) rty = promote(rty); p = tree(mkop(CALL, rty), rty, f, NULL); if (isptr(ty) || p->type->size > ty->size) p = cast(p, ty); } return p; } Tree vcall(Symbol func, Type ty, ...) { va_list ap; Tree args = NULL, e, f = pointer(idtree(func)), r = NULL; assert(isfunc(func->type)); if (ty == NULL) ty = freturn(func->type); va_start(ap, ty); while ((e = va_arg(ap, Tree)) != NULL) { if (hascall(e)) r = r == NULL ? e : tree(RIGHT, voidtype, r, e); args = tree(mkop(ARG, e->type), e->type, e, args); } va_end(ap); if (r != NULL) args = tree(RIGHT, voidtype, r, args); return calltree(f, ty, args, NULL); } int iscallb(Tree e) { return e->op == RIGHT && e->kids[0] && e->kids[1] && e->kids[0]->op == CALL+B && e->kids[1]->op == INDIR+B && isaddrop(e->kids[1]->kids[0]->op) && e->kids[1]->kids[0]->u.sym->temporary; } static Tree addtree(int op, Tree l, Tree r) { Type ty = inttype; if (isarith(l->type) && isarith(r->type)) { ty = binary(l->type, r->type); l = cast(l, ty); r = cast(r, ty); } else if (isptr(l->type) && isint(r->type)) return addtree(ADD, r, l); else if ( isptr(r->type) && isint(l->type) && !isfunc(r->type->type)) { long n; ty = unqual(r->type); n = unqual(ty->type)->size; if (n == 0) error("unknown size for type `%t'\n", ty->type); l = cast(l, promote(l->type)); if (n > 1) l = multree(MUL, cnsttree(signedptr, n), l); if (YYcheck && !isaddrop(r->op)) /* omit */ return nullcall(ty, YYcheck, r, l); /* omit */ return simplify(ADD, ty, l, r); } else typeerror(op, l, r); return simplify(op, ty, l, r); } Tree cnsttree(Type ty, ...) { Tree p = tree(mkop(CNST,ty), ty, NULL, NULL); va_list ap; va_start(ap, ty); switch (ty->op) { case INT: p->u.v.i = va_arg(ap, long); break; case UNSIGNED:p->u.v.u = va_arg(ap, unsigned long)&ones(8*ty->size); break; case FLOAT: p->u.v.d = va_arg(ap, double); break; case POINTER: p->u.v.p = va_arg(ap, void *); break; default: assert(0); } va_end(ap); return p; } Tree consttree(unsigned n, Type ty) { if (isarray(ty)) ty = atop(ty); else assert(isint(ty)); return cnsttree(ty, (unsigned long)n); } static Tree cmptree(int op, Tree l, Tree r) { Type ty; if (isarith(l->type) && isarith(r->type)) { ty = binary(l->type, r->type); l = cast(l, ty); r = cast(r, ty); } else if (compatible(l->type, r->type)) { ty = unsignedptr; l = cast(l, ty); r = cast(r, ty); } else { ty = unsignedtype; typeerror(op, l, r); } return simplify(mkop(op,ty), inttype, l, r); } static int compatible(Type ty1, Type ty2) { return isptr(ty1) && !isfunc(ty1->type) && isptr(ty2) && !isfunc(ty2->type) && eqtype(unqual(ty1->type), unqual(ty2->type), 0); } static int isnullptr(Tree e) { Type ty = unqual(e->type); return generic(e->op) == CNST && ((ty->op == INT && e->u.v.i == 0) || (ty->op == UNSIGNED && e->u.v.u == 0) || (isvoidptr(ty) && e->u.v.p == NULL)); } Tree eqtree(int op, Tree l, Tree r) { Type xty = l->type, yty = r->type; if ((isptr(xty) && isnullptr(r)) || (isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)) || (isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1))) { Type ty = unsignedptr; l = cast(l, ty); r = cast(r, ty); return simplify(mkop(op,ty), inttype, l, r); } if ((isptr(yty) && isnullptr(l)) || (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))) return eqtree(op, r, l); return cmptree(op, l, r); } Type assign(Type xty, Tree e) { Type yty = unqual(e->type); xty = unqual(xty); if (isenum(xty)) xty = xty->type; if (xty->size == 0 || yty->size == 0) return NULL; if ( (isarith(xty) && isarith(yty)) || (isstruct(xty) && xty == yty)) return xty; if (isptr(xty) && isnullptr(e)) return xty; if (((isvoidptr(xty) && isptr(yty)) || (isptr(xty) && isvoidptr(yty))) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) return xty; if ((isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1)) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) return xty; if (isptr(xty) && isptr(yty) && ( (isconst(xty->type) || !isconst(yty->type)) && (isvolatile(xty->type) || !isvolatile(yty->type)))) { Type lty = unqual(xty->type), rty = unqual(yty->type); if ((isenum(lty) && rty == inttype) || (isenum(rty) && lty == inttype)) { if (Aflag >= 1) warning("assignment between `%t' and `%t' is compiler-dependent\n", xty, yty); return xty; } } return NULL; } Tree asgntree(int op, Tree l, Tree r) { Type aty, ty; r = pointer(r); ty = assign(l->type, r); if (ty) r = cast(r, ty); else { typeerror(ASGN, l, r); if (r->type == voidtype) r = retype(r, inttype); ty = r->type; } if (l->op != FIELD) l = lvalue(l); aty = l->type; if (isptr(aty)) aty = unqual(aty)->type; if ( isconst(aty) || (isstruct(aty) && unqual(aty)->u.sym->u.s.cfields)) { if (isaddrop(l->op) && !l->u.sym->computed && !l->u.sym->generated) error("assignment to const identifier `%s'\n", l->u.sym->name); else error("assignment to const location\n"); } if (l->op == FIELD) { long n = 8*l->u.field->type->size - fieldsize(l->u.field); if (n > 0 && isunsigned(l->u.field->type)) r = bittree(BAND, r, cnsttree(r->type, (unsigned long)fieldmask(l->u.field))); else if (n > 0) { if (r->op == CNST+I) { n = r->u.v.i; if (n&(1<<(fieldsize(l->u.field)-1))) n |= ~0UL<u.field); r = cnsttree(r->type, n); } else r = shtree(RSH, shtree(LSH, r, cnsttree(inttype, n)), cnsttree(inttype, n)); } } if (isstruct(ty) && isaddrop(l->op) && iscallb(r)) return tree(RIGHT, ty, tree(CALL+B, ty, r->kids[0]->kids[0], l), idtree(l->u.sym)); return tree(mkop(op,ty), ty, l, r); } Tree condtree(Tree e, Tree l, Tree r) { Symbol t1; Type ty, xty = l->type, yty = r->type; Tree p; if (isarith(xty) && isarith(yty)) ty = binary(xty, yty); else if (eqtype(xty, yty, 1)) ty = unqual(xty); else if (isptr(xty) && isnullptr(r)) ty = xty; else if (isnullptr(l) && isptr(yty)) ty = yty; else if ((isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)) || (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))) ty = voidptype; else if ((isptr(xty) && isptr(yty) && eqtype(unqual(xty->type), unqual(yty->type), 1))) ty = xty; else { typeerror(COND, l, r); return consttree(0, inttype); } if (isptr(ty)) { ty = unqual(unqual(ty)->type); if ((isptr(xty) && isconst(unqual(xty)->type)) || (isptr(yty) && isconst(unqual(yty)->type))) ty = qual(CONST, ty); if ((isptr(xty) && isvolatile(unqual(xty)->type)) || (isptr(yty) && isvolatile(unqual(yty)->type))) ty = qual(VOLATILE, ty); ty = ptr(ty); } switch (e->op) { case CNST+I: return cast(e->u.v.i != 0 ? l : r, ty); case CNST+U: return cast(e->u.v.u != 0 ? l : r, ty); case CNST+P: return cast(e->u.v.p != 0 ? l : r, ty); case CNST+F: return cast(e->u.v.d != 0.0 ? l : r, ty); } if (ty != voidtype && ty->size > 0) { t1 = genident(REGISTER, unqual(ty), level); /* t1 = temporary(REGISTER, unqual(ty)); */ l = asgn(t1, l); r = asgn(t1, r); } else t1 = NULL; p = tree(COND, ty, cond(e), tree(RIGHT, ty, root(l), root(r))); p->u.sym = t1; return p; } /* addrof - address of p */ Tree addrof(Tree p) { Tree q = p; for (;;) switch (generic(q->op)) { case RIGHT: assert(q->kids[0] || q->kids[1]); q = q->kids[1] ? q->kids[1] : q->kids[0]; continue; case ASGN: q = q->kids[1]; continue; case COND: { Symbol t1 = q->u.sym; q->u.sym = 0; q = idtree(t1); /* fall thru */ } case INDIR: if (p == q) return q->kids[0]; q = q->kids[0]; return tree(RIGHT, q->type, root(p), q); default: error("addressable object required\n"); return value(p); } } /* andtree - construct tree for l [&& ||] r */ static Tree andtree(int op, Tree l, Tree r) { if (!isscalar(l->type) || !isscalar(r->type)) typeerror(op, l, r); return simplify(op, inttype, cond(l), cond(r)); } /* asgn - generate tree for assignment of expr e to symbol p sans qualifiers */ Tree asgn(Symbol p, Tree e) { if (isarray(p->type)) e = tree(ASGN+B, p->type, idtree(p), tree(INDIR+B, e->type, e, NULL)); else { Type ty = p->type; p->type = unqual(p->type); if (isstruct(p->type) && p->type->u.sym->u.s.cfields) { p->type->u.sym->u.s.cfields = 0; e = asgntree(ASGN, idtree(p), e); p->type->u.sym->u.s.cfields = 1; } else e = asgntree(ASGN, idtree(p), e); p->type = ty; } return e; } /* bittree - construct tree for l [& | ^ %] r */ Tree bittree(int op, Tree l, Tree r) { Type ty = inttype; if (isint(l->type) && isint(r->type)) { ty = binary(l->type, r->type); l = cast(l, ty); r = cast(r, ty); } else typeerror(op, l, r); return simplify(op, ty, l, r); } /* multree - construct tree for l [* /] r */ static Tree multree(int op, Tree l, Tree r) { Type ty = inttype; if (isarith(l->type) && isarith(r->type)) { ty = binary(l->type, r->type); l = cast(l, ty); r = cast(r, ty); } else typeerror(op, l, r); return simplify(op, ty, l, r); } /* shtree - construct tree for l [>> <<] r */ Tree shtree(int op, Tree l, Tree r) { Type ty = inttype; if (isint(l->type) && isint(r->type)) { ty = promote(l->type); l = cast(l, ty); r = cast(r, inttype); } else typeerror(op, l, r); return simplify(op, ty, l, r); } /* subtree - construct tree for l - r */ static Tree subtree(int op, Tree l, Tree r) { long n; Type ty = inttype; if (isarith(l->type) && isarith(r->type)) { ty = binary(l->type, r->type); l = cast(l, ty); r = cast(r, ty); } else if (isptr(l->type) && !isfunc(l->type->type) && isint(r->type)) { ty = unqual(l->type); n = unqual(ty->type)->size; if (n == 0) error("unknown size for type `%t'\n", ty->type); r = cast(r, promote(r->type)); if (n > 1) r = multree(MUL, cnsttree(signedptr, n), r); if (isunsigned(r->type)) r = cast(r, unsignedptr); else r = cast(r, signedptr); return simplify(SUB+P, ty, l, r); } else if (compatible(l->type, r->type)) { ty = unqual(l->type); n = unqual(ty->type)->size; if (n == 0) error("unknown size for type `%t'\n", ty->type); l = simplify(SUB+U, unsignedptr, cast(l, unsignedptr), cast(r, unsignedptr)); return simplify(DIV+I, longtype, cast(l, longtype), cnsttree(longtype, n)); } else typeerror(op, l, r); return simplify(op, ty, l, r); } /* typeerror - issue "operands of op have illegal types `l' and `r'" */ void typeerror(int op, Tree l, Tree r) { int i; static struct { int op; char *name; } ops[] = { {ASGN, "="}, {INDIR, "*"}, {NEG, "-"}, {ADD, "+"}, {SUB, "-"}, {LSH, "<<"}, {MOD, "%"}, {RSH, ">>"}, {BAND, "&"}, {BCOM, "~"}, {BOR, "|"}, {BXOR, "^"}, {DIV, "/"}, {MUL, "*"}, {EQ, "=="}, {GE, ">="}, {GT, ">"}, {LE, "<="}, {LT, "<"}, {NE, "!="}, {AND, "&&"}, {NOT, "!"}, {OR, "||"}, {COND, "?:"}, {0, 0} }; op = generic(op); for (i = 0; ops[i].op; i++) if (op == ops[i].op) break; assert(ops[i].name); if (r) error("operands of %s have illegal types `%t' and `%t'\n", ops[i].name, l->type, r->type); else error("operand of unary %s has illegal type `%t'\n", ops[i].name, l->type); } openarena_0.8.8.orig/code/tools/lcc/src/main.c0000644000175000017500000001501611656310263017722 0ustar smcvsmcv#include "c.h" static char rcsid[] = "main.c - faked rcsid"; static void typestab(Symbol, void *); static void stabline(Coordinate *); static void stabend(Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *); Interface *IR = NULL; int Aflag; /* >= 0 if -A specified */ int Pflag; /* != 0 if -P specified */ int glevel; /* == [0-9] if -g[0-9] specified */ int xref; /* != 0 for cross-reference data */ Symbol YYnull; /* _YYnull symbol if -n or -nvalidate specified */ Symbol YYcheck; /* _YYcheck symbol if -nvalidate,check specified */ static char *comment; static Interface stabIR; static char *currentfile; /* current file name */ static int currentline; /* current line number */ static FILE *srcfp; /* stream for current file, if non-NULL */ static int srcpos; /* position of srcfp, if srcfp is non-NULL */ int main(int argc, char *argv[]) { int i, j; for (i = argc - 1; i > 0; i--) if (strncmp(argv[i], "-target=", 8) == 0) break; if (i > 0) { char *s = strchr(argv[i], '\\'); if (s != NULL) *s = '/'; for (j = 0; bindings[j].name && bindings[j].ir; j++) if (strcmp(&argv[i][8], bindings[j].name) == 0) { IR = bindings[j].ir; break; } if (s != NULL) *s = '\\'; } if (!IR) { fprint(stderr, "%s: unknown target", argv[0]); if (i > 0) fprint(stderr, " `%s'", &argv[i][8]); fprint(stderr, "; must specify one of\n"); for (i = 0; bindings[i].name; i++) fprint(stderr, "\t-target=%s\n", bindings[i].name); exit(EXIT_FAILURE); } init(argc, argv); t = gettok(); (*IR->progbeg)(argc, argv); if (glevel && IR->stabinit) (*IR->stabinit)(firstfile, argc, argv); program(); if (events.end) apply(events.end, NULL, NULL); memset(&events, 0, sizeof events); if (glevel || xref) { Symbol symroot = NULL; Coordinate src; foreach(types, GLOBAL, typestab, &symroot); foreach(identifiers, GLOBAL, typestab, &symroot); src.file = firstfile; src.x = 0; src.y = lineno; if ((glevel > 2 || xref) && IR->stabend) (*IR->stabend)(&src, symroot, ltov(&loci, PERM), ltov(&symbols, PERM), NULL); else if (IR->stabend) (*IR->stabend)(&src, NULL, NULL, NULL, NULL); } finalize(); (*IR->progend)(); deallocate(PERM); return errcnt > 0; } /* main_init - process program arguments */ void main_init(int argc, char *argv[]) { char *infile = NULL, *outfile = NULL; int i; static int inited; if (inited) return; inited = 1; type_init(argc, argv); for (i = 1; i < argc; i++) if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "-g2") == 0) glevel = 2; else if (strncmp(argv[i], "-g", 2) == 0) { /* -gn[,x] */ char *p = strchr(argv[i], ','); glevel = atoi(argv[i]+2); if (p) { comment = p + 1; if (glevel == 0) glevel = 1; if (stabIR.stabline == NULL) { stabIR.stabline = IR->stabline; stabIR.stabend = IR->stabend; IR->stabline = stabline; IR->stabend = stabend; } } } else if (strcmp(argv[i], "-x") == 0) xref++; else if (strcmp(argv[i], "-A") == 0) { ++Aflag; } else if (strcmp(argv[i], "-P") == 0) Pflag++; else if (strcmp(argv[i], "-w") == 0) wflag++; else if (strcmp(argv[i], "-n") == 0) { if (!YYnull) { YYnull = install(string("_YYnull"), &globals, GLOBAL, PERM); YYnull->type = func(voidptype, NULL, 1); YYnull->sclass = EXTERN; (*IR->defsymbol)(YYnull); } } else if (strncmp(argv[i], "-n", 2) == 0) { /* -nvalid[,check] */ char *p = strchr(argv[i], ','); if (p) { YYcheck = install(string(p+1), &globals, GLOBAL, PERM); YYcheck->type = func(voidptype, NULL, 1); YYcheck->sclass = EXTERN; (*IR->defsymbol)(YYcheck); p = stringn(argv[i]+2, p - (argv[i]+2)); } else p = string(argv[i]+2); YYnull = install(p, &globals, GLOBAL, PERM); YYnull->type = func(voidptype, NULL, 1); YYnull->sclass = EXTERN; (*IR->defsymbol)(YYnull); } else if (strcmp(argv[i], "-v") == 0) fprint(stderr, "%s %s\n", argv[0], rcsid); else if (strncmp(argv[i], "-s", 2) == 0) density = strtod(&argv[i][2], NULL); else if (strncmp(argv[i], "-errout=", 8) == 0) { FILE *f = fopen(argv[i]+8, "w"); if (f == NULL) { fprint(stderr, "%s: can't write errors to `%s'\n", argv[0], argv[i]+8); exit(EXIT_FAILURE); } fclose(f); f = freopen(argv[i]+8, "w", stderr); assert(f); } else if (strncmp(argv[i], "-e", 2) == 0) { int x; if ((x = strtol(&argv[i][2], NULL, 0)) > 0) errlimit = x; } else if (strncmp(argv[i], "-little_endian=", 15) == 0) IR->little_endian = argv[i][15] - '0'; else if (strncmp(argv[i], "-mulops_calls=", 18) == 0) IR->mulops_calls = argv[i][18] - '0'; else if (strncmp(argv[i], "-wants_callb=", 13) == 0) IR->wants_callb = argv[i][13] - '0'; else if (strncmp(argv[i], "-wants_argb=", 12) == 0) IR->wants_argb = argv[i][12] - '0'; else if (strncmp(argv[i], "-left_to_right=", 15) == 0) IR->left_to_right = argv[i][15] - '0'; else if (strncmp(argv[i], "-wants_dag=", 11) == 0) IR->wants_dag = argv[i][11] - '0'; else if (*argv[i] != '-' || strcmp(argv[i], "-") == 0) { if (infile == NULL) infile = argv[i]; else if (outfile == NULL) outfile = argv[i]; } if (infile != NULL && strcmp(infile, "-") != 0 && freopen(infile, "r", stdin) == NULL) { fprint(stderr, "%s: can't read `%s'\n", argv[0], infile); exit(EXIT_FAILURE); } if (outfile != NULL && strcmp(outfile, "-") != 0 && freopen(outfile, "w", stdout) == NULL) { fprint(stderr, "%s: can't write `%s'\n", argv[0], outfile); exit(EXIT_FAILURE); } } /* typestab - emit stab entries for p */ static void typestab(Symbol p, void *cl) { if (*(Symbol *)cl == 0 && p->sclass && p->sclass != TYPEDEF) *(Symbol *)cl = p; if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype) (*IR->stabtype)(p); } /* stabline - emit source code for source coordinate *cp */ static void stabline(Coordinate *cp) { if (cp->file && cp->file != currentfile) { if (srcfp) fclose(srcfp); currentfile = cp->file; srcfp = fopen(currentfile, "r"); srcpos = 0; currentline = 0; } if (currentline != cp->y && srcfp) { char buf[512]; if (srcpos > cp->y) { rewind(srcfp); srcpos = 0; } for ( ; srcpos < cp->y; srcpos++) if (fgets(buf, sizeof buf, srcfp) == NULL) { fclose(srcfp); srcfp = NULL; break; } if (srcfp && srcpos == cp->y) print("%s%s", comment, buf); } currentline = cp->y; if (stabIR.stabline) (*stabIR.stabline)(cp); } static void stabend(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) { if (stabIR.stabend) (*stabIR.stabend)(cp, p, cpp, sp, stab); if (srcfp) fclose(srcfp); } openarena_0.8.8.orig/code/tools/lcc/src/profio.c0000644000175000017500000001461011656310262020272 0ustar smcvsmcv/* C compiler: prof.out input prof.out format: #files name ... (#files-1 times) #functions name file# x y count caller file x y ... (#functions-1 times) #points file# x y count ... (#points-1 times) */ #include "c.h" struct count { /* count data: */ int x, y; /* source coordinate */ int count; /* associated execution count */ }; #define MAXTOKEN 64 struct file { /* per-file prof.out data: */ struct file *link; /* link to next file */ char *name; /* file name */ int size; /* size of counts[] */ int count; /* counts[0..count-1] hold valid data */ struct count *counts; /* count data */ struct func { /* function data: */ struct func *link; /* link to next function */ char *name; /* function name */ struct count count; /* total number of calls */ struct caller { /* caller data: */ struct caller *link; /* link to next caller */ char *name; /* caller's name */ char *file; /* call site: file, x, y */ int x, y; int count; /* number of calls from this site */ } *callers; } *funcs; /* list of functions */ } *filelist; FILE *fp; /* acaller - add caller and site (file,x,y) to callee's callers list */ static void acaller(char *caller, char *file, int x, int y, int count, struct func *callee) { struct caller *q; assert(callee); for (q = callee->callers; q && (caller != q->name || file != q->file || x != q->x || y != q->y); q = q->link) ; if (!q) { struct caller **r; NEW(q, PERM); q->name = caller; q->file = file; q->x = x; q->y = y; q->count = 0; for (r = &callee->callers; *r && (strcmp(q->name, (*r)->name) > 0 || strcmp(q->file, (*r)->file) > 0 || q->y > (*r)->y || q->y > (*r)->y); r = &(*r)->link) ; q->link = *r; *r = q; } q->count += count; } /* compare - return <0, 0, >0 if ab, resp. */ static int compare(struct count *a, struct count *b) { if (a->y == b->y) return a->x - b->x; return a->y - b->y; } /* findfile - return file name's file list entry, or 0 */ static struct file *findfile(char *name) { struct file *p; for (p = filelist; p; p = p->link) if (p->name == name) return p; return 0; } /* afunction - add function name and its data to file's function list */ static struct func *afunction(char *name, char *file, int x, int y, int count) { struct file *p = findfile(file); struct func *q; assert(p); for (q = p->funcs; q && name != q->name; q = q->link) ; if (!q) { struct func **r; NEW(q, PERM); q->name = name; q->count.x = x; q->count.y = y; q->count.count = 0; q->callers = 0; for (r = &p->funcs; *r && compare(&q->count, &(*r)->count) > 0; r = &(*r)->link) ; q->link = *r; *r = q; } q->count.count += count; return q; } /* apoint - append execution point i to file's data */ static void apoint(int i, char *file, int x, int y, int count) { struct file *p = findfile(file); assert(p); if (i >= p->size) { int j; if (p->size == 0) { p->size = i >= 200 ? 2*i : 200; p->counts = newarray(p->size, sizeof *p->counts, PERM); } else { struct count *new; p->size = 2*i; new = newarray(p->size, sizeof *new, PERM); for (j = 0; j < p->count; j++) new[j] = p->counts[j]; p->counts = new; } for (j = p->count; j < p->size; j++) { static struct count z; p->counts[j] = z; } } p->counts[i].x = x; p->counts[i].y = y; p->counts[i].count += count; if (i >= p->count) p->count = i + 1; } /* findcount - return count associated with (file,x,y) or -1 */ int findcount(char *file, int x, int y) { static struct file *cursor; if (cursor == 0 || cursor->name != file) cursor = findfile(file); if (cursor) { int l, u; struct count *c = cursor->counts; for (l = 0, u = cursor->count - 1; l <= u; ) { int k = (l + u)/2; if (c[k].y > y || (c[k].y == y && c[k].x > x)) u = k - 1; else if (c[k].y < y || (c[k].y == y && c[k].x < x)) l = k + 1; else return c[k].count; } } return -1; } /* findfunc - return count associated with function name in file or -1 */ int findfunc(char *name, char *file) { static struct file *cursor; if (cursor == 0 || cursor->name != file) cursor = findfile(file); if (cursor) { struct func *p; for (p = cursor->funcs; p; p = p->link) if (p->name == name) return p->count.count; } return -1; } /* getd - read a nonnegative number */ static int getd(void) { int c, n = 0; while ((c = getc(fp)) != EOF && (c == ' ' || c == '\n' || c == '\t')) ; if (c >= '0' && c <= '9') { do n = 10*n + (c - '0'); while ((c = getc(fp)) >= '0' && c <= '9'); return n; } return -1; } /* getstr - read a string */ static char *getstr(void) { int c; char buf[MAXTOKEN], *s = buf; while ((c = getc(fp)) != EOF && c != ' ' && c != '\n' && c != '\t') if (s - buf < (int)sizeof buf - 2) *s++ = c; *s = 0; return s == buf ? (char *)0 : string(buf); } /* gather - read prof.out data from fd */ static int gather(void) { int i, nfiles, nfuncs, npoints; char *files[64]; if ((nfiles = getd()) < 0) return 0; assert(nfiles < NELEMS(files)); for (i = 0; i < nfiles; i++) { if ((files[i] = getstr()) == 0) return -1; if (!findfile(files[i])) { struct file *new; NEW(new, PERM); new->name = files[i]; new->size = new->count = 0; new->counts = 0; new->funcs = 0; new->link = filelist; filelist = new; } } if ((nfuncs = getd()) < 0) return -1; for (i = 0; i < nfuncs; i++) { struct func *q; char *name, *file; int f, x, y, count; if ((name = getstr()) == 0 || (f = getd()) <= 0 || (x = getd()) < 0 || (y = getd()) < 0 || (count = getd()) < 0) return -1; q = afunction(name, files[f-1], x, y, count); if ((name = getstr()) == 0 || (file = getstr()) == 0 || (x = getd()) < 0 || (y = getd()) < 0) return -1; if (*name != '?') acaller(name, file, x, y, count, q); } if ((npoints = getd()) < 0) return -1; for (i = 0; i < npoints; i++) { int f, x, y, count; if ((f = getd()) < 0 || (x = getd()) < 0 || (y = getd()) < 0 || (count = getd()) < 0) return -1; if (f) apoint(i, files[f-1], x, y, count); } return 1; } /* process - read prof.out data from file */ int process(char *file) { int more; if ((fp = fopen(file, "r")) != NULL) { struct file *p; while ((more = gather()) > 0) ; fclose(fp); if (more < 0) return more; for (p = filelist; p; p = p->link) qsort(p->counts, p->count, sizeof *p->counts, (int (*)(const void *, const void *)) compare); return 1; } return 0; } openarena_0.8.8.orig/code/tools/lcc/src/alloc.c0000644000175000017500000000351211656310263020066 0ustar smcvsmcv#include "c.h" struct block { struct block *next; char *limit; char *avail; }; union align { long l; char *p; double d; int (*f)(void); }; union header { struct block b; union align a; }; #ifdef PURIFY union header *arena[3]; void *allocate(unsigned long n, unsigned a) { union header *new = malloc(sizeof *new + n); assert(a < NELEMS(arena)); if (new == NULL) { error("insufficient memory\n"); exit(1); } new->b.next = (void *)arena[a]; arena[a] = new; return new + 1; } void deallocate(unsigned a) { union header *p, *q; assert(a < NELEMS(arena)); for (p = arena[a]; p; p = q) { q = (void *)p->b.next; free(p); } arena[a] = NULL; } void *newarray(unsigned long m, unsigned long n, unsigned a) { return allocate(m*n, a); } #else static struct block first[] = { { NULL }, { NULL }, { NULL } }, *arena[] = { &first[0], &first[1], &first[2] }; static struct block *freeblocks; void *allocate(unsigned long n, unsigned a) { struct block *ap; assert(a < NELEMS(arena)); assert(n > 0); ap = arena[a]; n = roundup(n, sizeof (union align)); while (n > ap->limit - ap->avail) { if ((ap->next = freeblocks) != NULL) { freeblocks = freeblocks->next; ap = ap->next; } else { unsigned m = sizeof (union header) + n + roundup(10*1024, sizeof (union align)); ap->next = malloc(m); ap = ap->next; if (ap == NULL) { error("insufficient memory\n"); exit(1); } ap->limit = (char *)ap + m; } ap->avail = (char *)((union header *)ap + 1); ap->next = NULL; arena[a] = ap; } ap->avail += n; return ap->avail - n; } void *newarray(unsigned long m, unsigned long n, unsigned a) { return allocate(m*n, a); } void deallocate(unsigned a) { assert(a < NELEMS(arena)); arena[a]->next = freeblocks; freeblocks = first[a].next; first[a].next = NULL; arena[a] = &first[a]; } #endif openarena_0.8.8.orig/code/tools/lcc/src/sym.c0000644000175000017500000001532411656310262017607 0ustar smcvsmcv#include "c.h" #include #define equalp(x) v.x == p->sym.u.c.v.x struct table { int level; Table previous; struct entry { struct symbol sym; struct entry *link; } *buckets[256]; Symbol all; }; #define HASHSIZE NELEMS(((Table)0)->buckets) static struct table cns = { CONSTANTS }, ext = { GLOBAL }, ids = { GLOBAL }, tys = { GLOBAL }; Table constants = &cns; Table externals = &ext; Table identifiers = &ids; Table globals = &ids; Table types = &tys; Table labels; int level = GLOBAL; static int tempid; List loci, symbols; Table table(Table tp, int level) { Table new; NEW0(new, FUNC); new->previous = tp; new->level = level; if (tp) new->all = tp->all; return new; } void foreach(Table tp, int lev, void (*apply)(Symbol, void *), void *cl) { assert(tp); while (tp && tp->level > lev) tp = tp->previous; if (tp && tp->level == lev) { Symbol p; Coordinate sav; sav = src; for (p = tp->all; p && p->scope == lev; p = p->up) { src = p->src; (*apply)(p, cl); } src = sav; } } void enterscope(void) { if (++level == LOCAL) tempid = 0; } void exitscope(void) { rmtypes(level); if (types->level == level) types = types->previous; if (identifiers->level == level) { if (Aflag >= 2) { int n = 0; Symbol p; for (p = identifiers->all; p && p->scope == level; p = p->up) if (++n > 127) { warning("more than 127 identifiers declared in a block\n"); break; } } identifiers = identifiers->previous; } assert(level >= GLOBAL); --level; } Symbol install(const char *name, Table *tpp, int level, int arena) { Table tp = *tpp; struct entry *p; unsigned h = (unsigned long)name&(HASHSIZE-1); assert(level == 0 || level >= tp->level); if (level > 0 && tp->level < level) tp = *tpp = table(tp, level); NEW0(p, arena); p->sym.name = (char *)name; p->sym.scope = level; p->sym.up = tp->all; tp->all = &p->sym; p->link = tp->buckets[h]; tp->buckets[h] = p; return &p->sym; } Symbol relocate(const char *name, Table src, Table dst) { struct entry *p, **q; Symbol *r; unsigned h = (unsigned long)name&(HASHSIZE-1); for (q = &src->buckets[h]; *q; q = &(*q)->link) if (name == (*q)->sym.name) break; assert(*q); /* Remove the entry from src's hash chain and from its list of all symbols. */ p = *q; *q = (*q)->link; for (r = &src->all; *r && *r != &p->sym; r = &(*r)->up) ; assert(*r == &p->sym); *r = p->sym.up; /* Insert the entry into dst's hash chain and into its list of all symbols. Return the symbol-table entry. */ p->link = dst->buckets[h]; dst->buckets[h] = p; p->sym.up = dst->all; dst->all = &p->sym; return &p->sym; } Symbol lookup(const char *name, Table tp) { struct entry *p; unsigned h = (unsigned long)name&(HASHSIZE-1); assert(tp); do for (p = tp->buckets[h]; p; p = p->link) if (name == p->sym.name) return &p->sym; while ((tp = tp->previous) != NULL); return NULL; } int genlabel(int n) { static int label = 1; label += n; return label - n; } Symbol findlabel(int lab) { struct entry *p; unsigned h = lab&(HASHSIZE-1); for (p = labels->buckets[h]; p; p = p->link) if (lab == p->sym.u.l.label) return &p->sym; NEW0(p, FUNC); p->sym.name = stringd(lab); p->sym.scope = LABELS; p->sym.up = labels->all; labels->all = &p->sym; p->link = labels->buckets[h]; labels->buckets[h] = p; p->sym.generated = 1; p->sym.u.l.label = lab; (*IR->defsymbol)(&p->sym); return &p->sym; } Symbol constant(Type ty, Value v) { struct entry *p; unsigned h = v.u&(HASHSIZE-1); ty = unqual(ty); for (p = constants->buckets[h]; p; p = p->link) if (eqtype(ty, p->sym.type, 1)) switch (ty->op) { case INT: if (equalp(i)) return &p->sym; break; case UNSIGNED: if (equalp(u)) return &p->sym; break; case FLOAT: if (equalp(d)) return &p->sym; break; case FUNCTION: if (equalp(g)) return &p->sym; break; case ARRAY: case POINTER: if (equalp(p)) return &p->sym; break; default: assert(0); } NEW0(p, PERM); p->sym.name = vtoa(ty, v); p->sym.scope = CONSTANTS; p->sym.type = ty; p->sym.sclass = STATIC; p->sym.u.c.v = v; p->link = constants->buckets[h]; p->sym.up = constants->all; constants->all = &p->sym; constants->buckets[h] = p; if (ty->u.sym && !ty->u.sym->addressed) (*IR->defsymbol)(&p->sym); p->sym.defined = 1; return &p->sym; } Symbol intconst(int n) { Value v; v.i = n; return constant(inttype, v); } Symbol genident(int scls, Type ty, int lev) { Symbol p; NEW0(p, lev >= LOCAL ? FUNC : PERM); p->name = stringd(genlabel(1)); p->scope = lev; p->sclass = scls; p->type = ty; p->generated = 1; if (lev == GLOBAL) (*IR->defsymbol)(p); return p; } Symbol temporary(int scls, Type ty) { Symbol p; NEW0(p, FUNC); p->name = stringd(++tempid); p->scope = level < LOCAL ? LOCAL : level; p->sclass = scls; p->type = ty; p->temporary = 1; p->generated = 1; return p; } Symbol newtemp(int sclass, int tc, int size) { Symbol p = temporary(sclass, btot(tc, size)); (*IR->local)(p); p->defined = 1; return p; } Symbol allsymbols(Table tp) { return tp->all; } void locus(Table tp, Coordinate *cp) { loci = append(cp, loci); symbols = append(allsymbols(tp), symbols); } void use(Symbol p, Coordinate src) { Coordinate *cp; NEW(cp, PERM); *cp = src; p->uses = append(cp, p->uses); } /* findtype - find type ty in identifiers */ Symbol findtype(Type ty) { Table tp = identifiers; int i; struct entry *p; assert(tp); do for (i = 0; i < HASHSIZE; i++) for (p = tp->buckets[i]; p; p = p->link) if (p->sym.type == ty && p->sym.sclass == TYPEDEF) return &p->sym; while ((tp = tp->previous) != NULL); return NULL; } /* mkstr - make a string constant */ Symbol mkstr(char *str) { Value v; Symbol p; v.p = str; p = constant(array(chartype, strlen(v.p) + 1, 0), v); if (p->u.c.loc == NULL) p->u.c.loc = genident(STATIC, p->type, GLOBAL); return p; } /* mksymbol - make a symbol for name, install in &globals if sclass==EXTERN */ Symbol mksymbol(int sclass, const char *name, Type ty) { Symbol p; if (sclass == EXTERN) p = install(string(name), &globals, GLOBAL, PERM); else { NEW0(p, PERM); p->name = string(name); p->scope = GLOBAL; } p->sclass = sclass; p->type = ty; (*IR->defsymbol)(p); p->defined = 1; return p; } /* vtoa - return string for the constant v of type ty */ char *vtoa(Type ty, Value v) { ty = unqual(ty); switch (ty->op) { case INT: return stringd(v.i); case UNSIGNED: return stringf((v.u&~0x7FFF) ? "0x%X" : "%U", v.u); case FLOAT: return stringf("%g", (double)v.d); case ARRAY: if (ty->type == chartype || ty->type == signedchar || ty->type == unsignedchar) return v.p; return stringf("%p", v.p); case POINTER: return stringf("%p", v.p); case FUNCTION: return stringf("%p", v.g); } assert(0); return NULL; } openarena_0.8.8.orig/code/tools/lcc/src/event.c0000644000175000017500000000064011656310263020114 0ustar smcvsmcv#include "c.h" struct entry { Apply func; void *cl; }; Events events; void attach(Apply func, void *cl, List *list) { struct entry *p; NEW(p, PERM); p->func = func; p->cl = cl; *list = append(p, *list); } void apply(List event, void *arg1, void *arg2) { if (event) { List lp = event; do { struct entry *p = lp->x; (*p->func)(p->cl, arg1, arg2); lp = lp->link; } while (lp != event); } } openarena_0.8.8.orig/code/tools/lcc/src/lex.c0000644000175000017500000005214511656310263017572 0ustar smcvsmcv#include "c.h" #include #include #define MAXTOKEN 32 enum { BLANK=01, NEWLINE=02, LETTER=04, DIGIT=010, HEX=020, OTHER=040 }; static unsigned char map[256] = { /* 000 nul */ 0, /* 001 soh */ 0, /* 002 stx */ 0, /* 003 etx */ 0, /* 004 eot */ 0, /* 005 enq */ 0, /* 006 ack */ 0, /* 007 bel */ 0, /* 010 bs */ 0, /* 011 ht */ BLANK, /* 012 nl */ NEWLINE, /* 013 vt */ BLANK, /* 014 ff */ BLANK, /* 015 cr */ 0, /* 016 so */ 0, /* 017 si */ 0, /* 020 dle */ 0, /* 021 dc1 */ 0, /* 022 dc2 */ 0, /* 023 dc3 */ 0, /* 024 dc4 */ 0, /* 025 nak */ 0, /* 026 syn */ 0, /* 027 etb */ 0, /* 030 can */ 0, /* 031 em */ 0, /* 032 sub */ 0, /* 033 esc */ 0, /* 034 fs */ 0, /* 035 gs */ 0, /* 036 rs */ 0, /* 037 us */ 0, /* 040 sp */ BLANK, /* 041 ! */ OTHER, /* 042 " */ OTHER, /* 043 # */ OTHER, /* 044 $ */ 0, /* 045 % */ OTHER, /* 046 & */ OTHER, /* 047 ' */ OTHER, /* 050 ( */ OTHER, /* 051 ) */ OTHER, /* 052 * */ OTHER, /* 053 + */ OTHER, /* 054 , */ OTHER, /* 055 - */ OTHER, /* 056 . */ OTHER, /* 057 / */ OTHER, /* 060 0 */ DIGIT, /* 061 1 */ DIGIT, /* 062 2 */ DIGIT, /* 063 3 */ DIGIT, /* 064 4 */ DIGIT, /* 065 5 */ DIGIT, /* 066 6 */ DIGIT, /* 067 7 */ DIGIT, /* 070 8 */ DIGIT, /* 071 9 */ DIGIT, /* 072 : */ OTHER, /* 073 ; */ OTHER, /* 074 < */ OTHER, /* 075 = */ OTHER, /* 076 > */ OTHER, /* 077 ? */ OTHER, /* 100 @ */ 0, /* 101 A */ LETTER|HEX, /* 102 B */ LETTER|HEX, /* 103 C */ LETTER|HEX, /* 104 D */ LETTER|HEX, /* 105 E */ LETTER|HEX, /* 106 F */ LETTER|HEX, /* 107 G */ LETTER, /* 110 H */ LETTER, /* 111 I */ LETTER, /* 112 J */ LETTER, /* 113 K */ LETTER, /* 114 L */ LETTER, /* 115 M */ LETTER, /* 116 N */ LETTER, /* 117 O */ LETTER, /* 120 P */ LETTER, /* 121 Q */ LETTER, /* 122 R */ LETTER, /* 123 S */ LETTER, /* 124 T */ LETTER, /* 125 U */ LETTER, /* 126 V */ LETTER, /* 127 W */ LETTER, /* 130 X */ LETTER, /* 131 Y */ LETTER, /* 132 Z */ LETTER, /* 133 [ */ OTHER, /* 134 \ */ OTHER, /* 135 ] */ OTHER, /* 136 ^ */ OTHER, /* 137 _ */ LETTER, /* 140 ` */ 0, /* 141 a */ LETTER|HEX, /* 142 b */ LETTER|HEX, /* 143 c */ LETTER|HEX, /* 144 d */ LETTER|HEX, /* 145 e */ LETTER|HEX, /* 146 f */ LETTER|HEX, /* 147 g */ LETTER, /* 150 h */ LETTER, /* 151 i */ LETTER, /* 152 j */ LETTER, /* 153 k */ LETTER, /* 154 l */ LETTER, /* 155 m */ LETTER, /* 156 n */ LETTER, /* 157 o */ LETTER, /* 160 p */ LETTER, /* 161 q */ LETTER, /* 162 r */ LETTER, /* 163 s */ LETTER, /* 164 t */ LETTER, /* 165 u */ LETTER, /* 166 v */ LETTER, /* 167 w */ LETTER, /* 170 x */ LETTER, /* 171 y */ LETTER, /* 172 z */ LETTER, /* 173 { */ OTHER, /* 174 | */ OTHER, /* 175 } */ OTHER, /* 176 ~ */ OTHER, }; static struct symbol tval; static char cbuf[BUFSIZE+1]; static unsigned int wcbuf[BUFSIZE+1]; Coordinate src; /* current source coordinate */ int t; char *token; /* current token */ Symbol tsym; /* symbol table entry for current token */ static void *cput(int c, void *cl); static void *wcput(int c, void *cl); static void *scon(int q, void *put(int c, void *cl), void *cl); static int backslash(int q); static Symbol fcon(void); static Symbol icon(unsigned long, int, int); static void ppnumber(char *); int gettok(void) { for (;;) { register unsigned char *rcp = cp; while (map[*rcp]&BLANK) rcp++; if (limit - rcp < MAXTOKEN) { cp = rcp; fillbuf(); rcp = cp; } src.file = file; src.x = (char *)rcp - line; src.y = lineno; cp = rcp + 1; switch (*rcp++) { case '/': if (*rcp == '*') { int c = 0; for (rcp++; *rcp != '/' || c != '*'; ) if (map[*rcp]&NEWLINE) { if (rcp < limit) c = *rcp; cp = rcp + 1; nextline(); rcp = cp; if (rcp == limit) break; } else c = *rcp++; if (rcp < limit) rcp++; else error("unclosed comment\n"); cp = rcp; continue; } return '/'; case '<': if (*rcp == '=') return cp++, LEQ; if (*rcp == '<') return cp++, LSHIFT; return '<'; case '>': if (*rcp == '=') return cp++, GEQ; if (*rcp == '>') return cp++, RSHIFT; return '>'; case '-': if (*rcp == '>') return cp++, DEREF; if (*rcp == '-') return cp++, DECR; return '-'; case '=': return *rcp == '=' ? cp++, EQL : '='; case '!': return *rcp == '=' ? cp++, NEQ : '!'; case '|': return *rcp == '|' ? cp++, OROR : '|'; case '&': return *rcp == '&' ? cp++, ANDAND : '&'; case '+': return *rcp == '+' ? cp++, INCR : '+'; case ';': case ',': case ':': case '*': case '~': case '%': case '^': case '?': case '[': case ']': case '{': case '}': case '(': case ')': return rcp[-1]; case '\n': case '\v': case '\r': case '\f': nextline(); if (cp == limit) { tsym = NULL; return EOI; } continue; case 'i': if (rcp[0] == 'f' && !(map[rcp[1]]&(DIGIT|LETTER))) { cp = rcp + 1; return IF; } if (rcp[0] == 'n' && rcp[1] == 't' && !(map[rcp[2]]&(DIGIT|LETTER))) { cp = rcp + 2; tsym = inttype->u.sym; return INT; } goto id; case 'h': case 'j': case 'k': case 'm': case 'n': case 'o': case 'p': case 'q': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': id: if (limit - rcp < MAXLINE) { cp = rcp - 1; fillbuf(); rcp = ++cp; } assert(cp == rcp); token = (char *)rcp - 1; while (map[*rcp]&(DIGIT|LETTER)) rcp++; token = stringn(token, (char *)rcp - token); tsym = lookup(token, identifiers); cp = rcp; return ID; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { unsigned long n = 0; if (limit - rcp < MAXLINE) { cp = rcp - 1; fillbuf(); rcp = ++cp; } assert(cp == rcp); token = (char *)rcp - 1; if (*token == '0' && (*rcp == 'x' || *rcp == 'X')) { int d, overflow = 0; while (*++rcp) { if (map[*rcp]&DIGIT) d = *rcp - '0'; else if (*rcp >= 'a' && *rcp <= 'f') d = *rcp - 'a' + 10; else if (*rcp >= 'A' && *rcp <= 'F') d = *rcp - 'A' + 10; else break; if (n&~(~0UL >> 4)) overflow = 1; else n = (n<<4) + d; } if ((char *)rcp - token <= 2) error("invalid hexadecimal constant `%S'\n", token, (char *)rcp-token); cp = rcp; tsym = icon(n, overflow, 16); } else if (*token == '0') { int err = 0, overflow = 0; for ( ; map[*rcp]&DIGIT; rcp++) { if (*rcp == '8' || *rcp == '9') err = 1; if (n&~(~0UL >> 3)) overflow = 1; else n = (n<<3) + (*rcp - '0'); } if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') { cp = rcp; tsym = fcon(); return FCON; } cp = rcp; tsym = icon(n, overflow, 8); if (err) error("invalid octal constant `%S'\n", token, (char*)cp-token); } else { int overflow = 0; for (n = *token - '0'; map[*rcp]&DIGIT; ) { int d = *rcp++ - '0'; if (n > (ULONG_MAX - d)/10) overflow = 1; else n = 10*n + d; } if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') { cp = rcp; tsym = fcon(); return FCON; } cp = rcp; tsym = icon(n, overflow, 10); } return ICON; } case '.': if (rcp[0] == '.' && rcp[1] == '.') { cp += 2; return ELLIPSIS; } if ((map[*rcp]&DIGIT) == 0) return '.'; if (limit - rcp < MAXLINE) { cp = rcp - 1; fillbuf(); rcp = ++cp; } assert(cp == rcp); cp = rcp - 1; token = (char *)cp; tsym = fcon(); return FCON; case 'L': if (*rcp == '\'') { unsigned int *s = scon(*cp, wcput, wcbuf); if (s - wcbuf > 2) warning("excess characters in wide-character literal ignored\n"); tval.type = widechar; tval.u.c.v.u = wcbuf[0]; tsym = &tval; return ICON; } else if (*rcp == '"') { unsigned int *s = scon(*cp, wcput, wcbuf); tval.type = array(widechar, s - wcbuf, 0); tval.u.c.v.p = wcbuf; tsym = &tval; return SCON; } else goto id; case '\'': { char *s = scon(*--cp, cput, cbuf); if (s - cbuf > 2) warning("excess characters in multibyte character literal ignored\n"); tval.type = inttype; if (chartype->op == INT) tval.u.c.v.i = extend(cbuf[0], chartype); else tval.u.c.v.i = cbuf[0]&0xFF; tsym = &tval; return ICON; } case '"': { char *s = scon(*--cp, cput, cbuf); tval.type = array(chartype, s - cbuf, 0); tval.u.c.v.p = cbuf; tsym = &tval; return SCON; } case 'a': if (rcp[0] == 'u' && rcp[1] == 't' && rcp[2] == 'o' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return AUTO; } goto id; case 'b': if (rcp[0] == 'r' && rcp[1] == 'e' && rcp[2] == 'a' && rcp[3] == 'k' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; return BREAK; } goto id; case 'c': if (rcp[0] == 'a' && rcp[1] == 's' && rcp[2] == 'e' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return CASE; } if (rcp[0] == 'h' && rcp[1] == 'a' && rcp[2] == 'r' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; tsym = chartype->u.sym; return CHAR; } if (rcp[0] == 'o' && rcp[1] == 'n' && rcp[2] == 's' && rcp[3] == 't' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; return CONST; } if (rcp[0] == 'o' && rcp[1] == 'n' && rcp[2] == 't' && rcp[3] == 'i' && rcp[4] == 'n' && rcp[5] == 'u' && rcp[6] == 'e' && !(map[rcp[7]]&(DIGIT|LETTER))) { cp = rcp + 7; return CONTINUE; } goto id; case 'd': if (rcp[0] == 'e' && rcp[1] == 'f' && rcp[2] == 'a' && rcp[3] == 'u' && rcp[4] == 'l' && rcp[5] == 't' && !(map[rcp[6]]&(DIGIT|LETTER))) { cp = rcp + 6; return DEFAULT; } if (rcp[0] == 'o' && rcp[1] == 'u' && rcp[2] == 'b' && rcp[3] == 'l' && rcp[4] == 'e' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; tsym = doubletype->u.sym; return DOUBLE; } if (rcp[0] == 'o' && !(map[rcp[1]]&(DIGIT|LETTER))) { cp = rcp + 1; return DO; } goto id; case 'e': if (rcp[0] == 'l' && rcp[1] == 's' && rcp[2] == 'e' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return ELSE; } if (rcp[0] == 'n' && rcp[1] == 'u' && rcp[2] == 'm' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return ENUM; } if (rcp[0] == 'x' && rcp[1] == 't' && rcp[2] == 'e' && rcp[3] == 'r' && rcp[4] == 'n' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return EXTERN; } goto id; case 'f': if (rcp[0] == 'l' && rcp[1] == 'o' && rcp[2] == 'a' && rcp[3] == 't' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; tsym = floattype->u.sym; return FLOAT; } if (rcp[0] == 'o' && rcp[1] == 'r' && !(map[rcp[2]]&(DIGIT|LETTER))) { cp = rcp + 2; return FOR; } goto id; case 'g': if (rcp[0] == 'o' && rcp[1] == 't' && rcp[2] == 'o' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return GOTO; } goto id; case 'l': if (rcp[0] == 'o' && rcp[1] == 'n' && rcp[2] == 'g' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; return LONG; } goto id; case 'r': if (rcp[0] == 'e' && rcp[1] == 'g' && rcp[2] == 'i' && rcp[3] == 's' && rcp[4] == 't' && rcp[5] == 'e' && rcp[6] == 'r' && !(map[rcp[7]]&(DIGIT|LETTER))) { cp = rcp + 7; return REGISTER; } if (rcp[0] == 'e' && rcp[1] == 't' && rcp[2] == 'u' && rcp[3] == 'r' && rcp[4] == 'n' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return RETURN; } goto id; case 's': if (rcp[0] == 'h' && rcp[1] == 'o' && rcp[2] == 'r' && rcp[3] == 't' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; return SHORT; } if (rcp[0] == 'i' && rcp[1] == 'g' && rcp[2] == 'n' && rcp[3] == 'e' && rcp[4] == 'd' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return SIGNED; } if (rcp[0] == 'i' && rcp[1] == 'z' && rcp[2] == 'e' && rcp[3] == 'o' && rcp[4] == 'f' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return SIZEOF; } if (rcp[0] == 't' && rcp[1] == 'a' && rcp[2] == 't' && rcp[3] == 'i' && rcp[4] == 'c' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return STATIC; } if (rcp[0] == 't' && rcp[1] == 'r' && rcp[2] == 'u' && rcp[3] == 'c' && rcp[4] == 't' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return STRUCT; } if (rcp[0] == 'w' && rcp[1] == 'i' && rcp[2] == 't' && rcp[3] == 'c' && rcp[4] == 'h' && !(map[rcp[5]]&(DIGIT|LETTER))) { cp = rcp + 5; return SWITCH; } goto id; case 't': if (rcp[0] == 'y' && rcp[1] == 'p' && rcp[2] == 'e' && rcp[3] == 'd' && rcp[4] == 'e' && rcp[5] == 'f' && !(map[rcp[6]]&(DIGIT|LETTER))) { cp = rcp + 6; return TYPEDEF; } goto id; case 'u': if (rcp[0] == 'n' && rcp[1] == 'i' && rcp[2] == 'o' && rcp[3] == 'n' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; return UNION; } if (rcp[0] == 'n' && rcp[1] == 's' && rcp[2] == 'i' && rcp[3] == 'g' && rcp[4] == 'n' && rcp[5] == 'e' && rcp[6] == 'd' && !(map[rcp[7]]&(DIGIT|LETTER))) { cp = rcp + 7; return UNSIGNED; } goto id; case 'v': if (rcp[0] == 'o' && rcp[1] == 'i' && rcp[2] == 'd' && !(map[rcp[3]]&(DIGIT|LETTER))) { cp = rcp + 3; tsym = voidtype->u.sym; return VOID; } if (rcp[0] == 'o' && rcp[1] == 'l' && rcp[2] == 'a' && rcp[3] == 't' && rcp[4] == 'i' && rcp[5] == 'l' && rcp[6] == 'e' && !(map[rcp[7]]&(DIGIT|LETTER))) { cp = rcp + 7; return VOLATILE; } goto id; case 'w': if (rcp[0] == 'h' && rcp[1] == 'i' && rcp[2] == 'l' && rcp[3] == 'e' && !(map[rcp[4]]&(DIGIT|LETTER))) { cp = rcp + 4; return WHILE; } goto id; case '_': if (rcp[0] == '_' && rcp[1] == 't' && rcp[2] == 'y' && rcp[3] == 'p' && rcp[4] == 'e' && rcp[5] == 'c' && rcp[6] == 'o' && rcp[7] == 'd' && rcp[8] == 'e' && !(map[rcp[9]]&(DIGIT|LETTER))) { cp = rcp + 9; return TYPECODE; } if (rcp[0] == '_' && rcp[1] == 'f' && rcp[2] == 'i' && rcp[3] == 'r' && rcp[4] == 's' && rcp[5] == 't' && rcp[6] == 'a' && rcp[7] == 'r' && rcp[8] == 'g' && !(map[rcp[9]]&(DIGIT|LETTER))) { cp = rcp + 9; return FIRSTARG; } goto id; default: if ((map[cp[-1]]&BLANK) == 0) { if (cp[-1] < ' ' || cp[-1] >= 0177) error("illegal character `\\0%o'\n", cp[-1]); else error("illegal character `%c'\n", cp[-1]); } } } } static Symbol icon(unsigned long n, int overflow, int base) { if (((*cp=='u'||*cp=='U') && (cp[1]=='l'||cp[1]=='L')) || ((*cp=='l'||*cp=='L') && (cp[1]=='u'||cp[1]=='U'))) { tval.type = unsignedlong; cp += 2; } else if (*cp == 'u' || *cp == 'U') { if (overflow || n > unsignedtype->u.sym->u.limits.max.i) tval.type = unsignedlong; else tval.type = unsignedtype; cp += 1; } else if (*cp == 'l' || *cp == 'L') { if (overflow || n > longtype->u.sym->u.limits.max.i) tval.type = unsignedlong; else tval.type = longtype; cp += 1; } else if (overflow || n > longtype->u.sym->u.limits.max.i) tval.type = unsignedlong; else if (n > inttype->u.sym->u.limits.max.i) tval.type = longtype; else if (base != 10 && n > inttype->u.sym->u.limits.max.i) tval.type = unsignedtype; else tval.type = inttype; switch (tval.type->op) { case INT: if (overflow || n > tval.type->u.sym->u.limits.max.i) { warning("overflow in constant `%S'\n", token, (char*)cp - token); tval.u.c.v.i = tval.type->u.sym->u.limits.max.i; } else tval.u.c.v.i = n; break; case UNSIGNED: if (overflow || n > tval.type->u.sym->u.limits.max.u) { warning("overflow in constant `%S'\n", token, (char*)cp - token); tval.u.c.v.u = tval.type->u.sym->u.limits.max.u; } else tval.u.c.v.u = n; break; default: assert(0); } ppnumber("integer"); return &tval; } static void ppnumber(char *which) { unsigned char *rcp = cp--; for ( ; (map[*cp]&(DIGIT|LETTER)) || *cp == '.'; cp++) if ((cp[0] == 'E' || cp[0] == 'e') && (cp[1] == '-' || cp[1] == '+')) cp++; if (cp > rcp) error("`%S' is a preprocessing number but an invalid %s constant\n", token, (char*)cp-token, which); } static Symbol fcon(void) { if (*cp == '.') do cp++; while (map[*cp]&DIGIT); if (*cp == 'e' || *cp == 'E') { if (*++cp == '-' || *cp == '+') cp++; if (map[*cp]&DIGIT) do cp++; while (map[*cp]&DIGIT); else error("invalid floating constant `%S'\n", token, (char*)cp - token); } errno = 0; tval.u.c.v.d = strtod(token, NULL); if (errno == ERANGE) warning("overflow in floating constant `%S'\n", token, (char*)cp - token); if (*cp == 'f' || *cp == 'F') { ++cp; if (tval.u.c.v.d > floattype->u.sym->u.limits.max.d) warning("overflow in floating constant `%S'\n", token, (char*)cp - token); tval.type = floattype; } else if (*cp == 'l' || *cp == 'L') { cp++; tval.type = longdouble; } else { if (tval.u.c.v.d > doubletype->u.sym->u.limits.max.d) warning("overflow in floating constant `%S'\n", token, (char*)cp - token); tval.type = doubletype; } ppnumber("floating"); return &tval; } static void *cput(int c, void *cl) { char *s = cl; if (c < 0 || c > 255) warning("overflow in escape sequence with resulting value `%d'\n", c); *s++ = c; return s; } static void *wcput(int c, void *cl) { unsigned int *s = cl; *s++ = c; return s; } static void *scon(int q, void *put(int c, void *cl), void *cl) { int n = 0, nbad = 0; do { cp++; while (*cp != q) { int c; if (map[*cp]&NEWLINE) { if (cp < limit) break; cp++; nextline(); if (cp == limit) break; continue; } c = *cp++; if (c == '\\') { if (map[*cp]&NEWLINE) { if (cp < limit) break; cp++; nextline(); } if (limit - cp < MAXTOKEN) fillbuf(); c = backslash(q); } else if (c < 0 || c > 255 || map[c] == 0) nbad++; if (n++ < BUFSIZE) cl = put(c, cl); } if (*cp == q) cp++; else error("missing %c\n", q); } while (q == '"' && getchr() == '"'); cl = put(0, cl); if (n >= BUFSIZE) error("%s literal too long\n", q == '"' ? "string" : "character"); if (Aflag >= 2 && q == '"' && n > 509) warning("more than 509 characters in a string literal\n"); if (Aflag >= 2 && nbad > 0) warning("%s literal contains non-portable characters\n", q == '"' ? "string" : "character"); return cl; } int getchr(void) { for (;;) { while (map[*cp]&BLANK) cp++; if (!(map[*cp]&NEWLINE)) return *cp; cp++; nextline(); if (cp == limit) return EOI; } } static int backslash(int q) { unsigned int c; switch (*cp++) { case 'a': return 7; case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'v': return '\v'; case '\'': case '"': case '\\': case '\?': break; case 'x': { int overflow = 0; if ((map[*cp]&(DIGIT|HEX)) == 0) { if (*cp < ' ' || *cp == 0177) error("ill-formed hexadecimal escape sequence\n"); else error("ill-formed hexadecimal escape sequence `\\x%c'\n", *cp); if (*cp != q) cp++; return 0; } for (c = 0; map[*cp]&(DIGIT|HEX); cp++) { if (c >> (8*widechar->size - 4)) overflow = 1; if (map[*cp]&DIGIT) c = (c<<4) + *cp - '0'; else c = (c<<4) + (*cp&~040) - 'A' + 10; } if (overflow) warning("overflow in hexadecimal escape sequence\n"); return c&ones(8*widechar->size); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = *(cp-1) - '0'; if (*cp >= '0' && *cp <= '7') { c = (c<<3) + *cp++ - '0'; if (*cp >= '0' && *cp <= '7') c = (c<<3) + *cp++ - '0'; } return c; default: if (cp[-1] < ' ' || cp[-1] >= 0177) warning("unrecognized character escape sequence\n"); else warning("unrecognized character escape sequence `\\%c'\n", cp[-1]); } return cp[-1]; } openarena_0.8.8.orig/code/tools/lcc/src/init.c0000644000175000017500000001716611656310262017750 0ustar smcvsmcv#include "c.h" static int curseg; /* current segment */ /* defpointer - initialize a pointer to p or to 0 if p==0 */ void defpointer(Symbol p) { if (p) { (*IR->defaddress)(p); p->ref++; } else { static Value v; (*IR->defconst)(P, voidptype->size, v); } } /* genconst - generate/check constant expression e; return size */ static int genconst(Tree e, int def) { for (;;) switch (generic(e->op)) { case ADDRG: if (def) (*IR->defaddress)(e->u.sym); return e->type->size; case CNST: if (e->op == CNST+P && isarray(e->type)) { e = cvtconst(e); continue; } if (def) (*IR->defconst)(e->type->op, e->type->size, e->u.v); return e->type->size; case RIGHT: assert(e->kids[0] || e->kids[1]); if (e->kids[1] && e->kids[0]) error("initializer must be constant\n"); e = e->kids[1] ? e->kids[1] : e->kids[0]; continue; case CVP: if (isarith(e->type)) error("cast from `%t' to `%t' is illegal in constant expressions\n", e->kids[0]->type, e->type); /* fall thru */ case CVI: case CVU: case CVF: e = e->kids[0]; continue; default: error("initializer must be constant\n"); if (def) genconst(consttree(0, inttype), def); return inttype->size; } } /* initvalue - evaluate a constant expression for a value of integer type ty */ static Tree initvalue(Type ty) { Type aty; Tree e; needconst++; e = expr1(0); if ((aty = assign(ty, e)) != NULL) e = cast(e, aty); else { error("invalid initialization type; found `%t' expected `%t'\n", e->type, ty); e = retype(consttree(0, inttype), ty); } needconst--; if (generic(e->op) != CNST) { error("initializer must be constant\n"); e = retype(consttree(0, inttype), ty); } return e; } /* initarray - initialize array of ty of <= len bytes; if len == 0, go to } */ static int initarray(int len, Type ty, int lev) { int n = 0; do { initializer(ty, lev); n += ty->size; if ((len > 0 && n >= len) || t != ',') break; t = gettok(); } while (t != '}'); return n; } /* initchar - initialize array of <= len ty characters; if len == 0, go to } */ static int initchar(int len, Type ty) { int n = 0; char buf[16], *s = buf; do { *s++ = initvalue(ty)->u.v.i; if (++n%inttype->size == 0) { (*IR->defstring)(inttype->size, buf); s = buf; } if ((len > 0 && n >= len) || t != ',') break; t = gettok(); } while (t != '}'); if (s > buf) (*IR->defstring)(s - buf, buf); return n; } /* initend - finish off an initialization at level lev; accepts trailing comma */ static void initend(int lev, char follow[]) { if (lev == 0 && t == ',') t = gettok(); test('}', follow); } /* initfields - initialize <= an unsigned's worth of bit fields in fields p to q */ static int initfields(Field p, Field q) { unsigned int bits = 0; int i, n = 0; do { i = initvalue(inttype)->u.v.i; if (fieldsize(p) < 8*p->type->size) { if ((p->type == inttype && (i < -(int)(fieldmask(p)>>1)-1 || i > (int)(fieldmask(p)>>1))) || (p->type == unsignedtype && (i&~fieldmask(p)) != 0)) warning("initializer exceeds bit-field width\n"); i &= fieldmask(p); } bits |= i<little_endian) { if (fieldsize(p) + fieldright(p) > n) n = fieldsize(p) + fieldright(p); } else { if (fieldsize(p) + fieldleft(p) > n) n = fieldsize(p) + fieldleft(p); } if (p->link == q) break; p = p->link; } while (t == ',' && (t = gettok()) != 0); n = (n + 7)/8; for (i = 0; i < n; i++) { Value v; if (IR->little_endian) { v.u = (unsigned char)bits; bits >>= 8; } else { /* a big endian */ v.u = (unsigned char)(bits>>(8*(unsignedtype->size - 1))); bits <<= 8; } (*IR->defconst)(U, unsignedchar->size, v); } return n; } /* initstruct - initialize a struct ty of <= len bytes; if len == 0, go to } */ static int initstruct(int len, Type ty, int lev) { int a, n = 0; Field p = ty->u.sym->u.s.flist; do { if (p->offset > n) { (*IR->space)(p->offset - n); n += p->offset - n; } if (p->lsb) { Field q = p; while (q->link && q->link->offset == p->offset) q = q->link; n += initfields(p, q->link); p = q; } else { initializer(p->type, lev); n += p->type->size; } if (p->link) { p = p->link; a = p->type->align; } else a = ty->align; if (a && n%a) { (*IR->space)(a - n%a); n = roundup(n, a); } if ((len > 0 && n >= len) || t != ',') break; t = gettok(); } while (t != '}'); return n; } /* initializer - constexpr | { constexpr ( , constexpr )* [ , ] } */ Type initializer(Type ty, int lev) { int n = 0; Tree e; Type aty = NULL; static char follow[] = { IF, CHAR, STATIC, 0 }; ty = unqual(ty); if (isscalar(ty)) { needconst++; if (t == '{') { t = gettok(); e = expr1(0); initend(lev, follow); } else e = expr1(0); e = pointer(e); if ((aty = assign(ty, e)) != NULL) e = cast(e, aty); else error("invalid initialization type; found `%t' expected `%t'\n", e->type, ty); n = genconst(e, 1); deallocate(STMT); needconst--; } if ((isunion(ty) || isstruct(ty)) && ty->size == 0) { static char follow[] = { CHAR, STATIC, 0 }; error("cannot initialize undefined `%t'\n", ty); skipto(';', follow); return ty; } else if (isunion(ty)) { if (t == '{') { t = gettok(); n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); initend(lev, follow); } else { if (lev == 0) error("missing { in initialization of `%t'\n", ty); n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); } } else if (isstruct(ty)) { if (t == '{') { t = gettok(); n = initstruct(0, ty, lev + 1); test('}', follow); } else if (lev > 0) n = initstruct(ty->size, ty, lev + 1); else { error("missing { in initialization of `%t'\n", ty); n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1); } } if (isarray(ty)) aty = unqual(ty->type); if (isarray(ty) && ischar(aty)) { if (t == SCON) { if (ty->size > 0 && ty->size == tsym->type->size - 1) tsym->type = array(chartype, ty->size, 0); n = tsym->type->size; (*IR->defstring)(tsym->type->size, tsym->u.c.v.p); t = gettok(); } else if (t == '{') { t = gettok(); if (t == SCON) { ty = initializer(ty, lev + 1); initend(lev, follow); return ty; } n = initchar(0, aty); test('}', follow); } else if (lev > 0 && ty->size > 0) n = initchar(ty->size, aty); else { /* eg, char c[] = 0; */ error("missing { in initialization of `%t'\n", ty); n = initchar(1, aty); } } else if (isarray(ty)) { if (t == SCON && aty == widechar) { int i; unsigned int *s = tsym->u.c.v.p; if (ty->size > 0 && ty->size == tsym->type->size - widechar->size) tsym->type = array(widechar, ty->size/widechar->size, 0); n = tsym->type->size; for (i = 0; i < n; i += widechar->size) { Value v; v.u = *s++; (*IR->defconst)(widechar->op, widechar->size, v); } t = gettok(); } else if (t == '{') { t = gettok(); if (t == SCON && aty == widechar) { ty = initializer(ty, lev + 1); initend(lev, follow); return ty; } n = initarray(0, aty, lev + 1); test('}', follow); } else if (lev > 0 && ty->size > 0) n = initarray(ty->size, aty, lev + 1); else { error("missing { in initialization of `%t'\n", ty); n = initarray(aty->size, aty, lev + 1); } } if (ty->size) { if (n > ty->size) error("too many initializers\n"); else if (n < ty->size) (*IR->space)(ty->size - n); } else if (isarray(ty) && ty->type->size > 0) ty = array(ty->type, n/ty->type->size, 0); else ty->size = n; return ty; } /* swtoseg - switch to segment seg, if necessary */ void swtoseg(int seg) { if (curseg != seg) (*IR->segment)(seg); curseg = seg; } openarena_0.8.8.orig/code/tools/lcc/src/output.c0000644000175000017500000000633411656310262020340 0ustar smcvsmcv#include "c.h" static char *outs(const char *str, FILE *f, char *bp) { if (f) fputs(str, f); else while ((*bp = *str++)) bp++; return bp; } static char *outd(long n, FILE *f, char *bp) { unsigned long m; char buf[25], *s = buf + sizeof buf; *--s = '\0'; if (n < 0) m = -n; else m = n; do *--s = m%10 + '0'; while ((m /= 10) != 0); if (n < 0) *--s = '-'; return outs(s, f, bp); } static char *outu(unsigned long n, int base, FILE *f, char *bp) { char buf[25], *s = buf + sizeof buf; *--s = '\0'; do *--s = "0123456789abcdef"[n%base]; while ((n /= base) != 0); return outs(s, f, bp); } void print(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprint(stdout, NULL, fmt, ap); va_end(ap); } /* fprint - formatted output to f */ void fprint(FILE *f, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprint(f, NULL, fmt, ap); va_end(ap); } /* stringf - formatted output to a saved string */ char *stringf(const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vfprint(NULL, buf, fmt, ap); va_end(ap); return string(buf); } /* vfprint - formatted output to f or string bp */ void vfprint(FILE *f, char *bp, const char *fmt, va_list ap) { for (; *fmt; fmt++) if (*fmt == '%') switch (*++fmt) { case 'd': bp = outd(va_arg(ap, int), f, bp); break; case 'D': bp = outd(va_arg(ap, long), f, bp); break; case 'U': bp = outu(va_arg(ap, unsigned long), 10, f, bp); break; case 'u': bp = outu(va_arg(ap, unsigned), 10, f, bp); break; case 'o': bp = outu(va_arg(ap, unsigned), 8, f, bp); break; case 'X': bp = outu(va_arg(ap, unsigned long), 16, f, bp); break; case 'x': bp = outu(va_arg(ap, unsigned), 16, f, bp); break; case 'f': case 'e': case 'g': { static char format[] = "%f"; char buf[128]; format[1] = *fmt; sprintf(buf, format, va_arg(ap, double)); bp = outs(buf, f, bp); } ; break; case 's': bp = outs(va_arg(ap, char *), f, bp); break; case 'p': { void *p = va_arg(ap, void *); if (p) bp = outs("0x", f, bp); bp = outu((unsigned long)p, 16, f, bp); break; } case 'c': if (f) fputc(va_arg(ap, int), f); else *bp++ = va_arg(ap, int); break; case 'S': { char *s = va_arg(ap, char *); int n = va_arg(ap, int); if (s) { for ( ; n-- > 0; s++) if (f) (void)putc(*s, f); else *bp++ = *s; } } break; case 'k': { int t = va_arg(ap, int); static char *tokens[] = { #define xx(a,b,c,d,e,f,g) g, #define yy(a,b,c,d,e,f,g) g, #include "token.h" }; assert(tokens[t&0177]); bp = outs(tokens[t&0177], f, bp); } break; case 't': { Type ty = va_arg(ap, Type); assert(f); outtype(ty ? ty : voidtype, f); } break; case 'w': { Coordinate *p = va_arg(ap, Coordinate *); if (p->file && *p->file) { bp = outs(p->file, f, bp); bp = outs(":", f, bp); } bp = outd(p->y, f, bp); } break; case 'I': { int n = va_arg(ap, int); while (--n >= 0) if (f) (void)putc(' ', f); else *bp++ = ' '; } break; default: if (f) (void)putc(*fmt, f); else *bp++ = *fmt; break; } else if (f) (void)putc(*fmt, f); else *bp++ = *fmt; if (!f) *bp = '\0'; } openarena_0.8.8.orig/code/tools/lcc/src/prof.c0000644000175000017500000001437111656310263017747 0ustar smcvsmcv#include "c.h" struct callsite { char *file, *name; union coordinate { unsigned int coord; struct { unsigned int y:16,x:10,index:6; } le; struct { unsigned int index:6,x:10,y:16; } be; } u; }; struct func { struct func *link; struct caller *callers; char *name; union coordinate src; }; struct map { /* source code map; 200 coordinates/map */ int size; union coordinate u[200]; }; int npoints; /* # of execution points if -b specified */ int ncalled = -1; /* #times prof.out says current function was called */ static Symbol YYlink; /* symbol for file's struct _bbdata */ static Symbol YYcounts; /* symbol for _YYcounts if -b specified */ static List maplist; /* list of struct map *'s */ static List filelist; /* list of file names */ static Symbol funclist; /* list of struct func *'s */ static Symbol afunc; /* current function's struct func */ /* bbcall - build tree to set _callsite at call site *cp, emit call site data */ static void bbcall(Symbol yycounts, Coordinate *cp, Tree *e) { static Symbol caller; Value v; union coordinate u; Symbol p = genident(STATIC, array(voidptype, 0, 0), GLOBAL); Tree t = *e; defglobal(p, LIT); defpointer(cp->file ? mkstr(cp->file)->u.c.loc : (Symbol)0); defpointer(mkstr(cfunc->name)->u.c.loc); if (IR->little_endian) { u.le.x = cp->x; u.le.y = cp->y; } else { u.be.x = cp->x; u.be.y = cp->y; } (*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v)); if (caller == 0) { caller = mksymbol(EXTERN, "_caller", ptr(voidptype)); caller->defined = 0; } if (generic((*e)->op) != CALL) t = (*e)->kids[0]; assert(generic(t->op) == CALL); t = tree(t->op, t->type, tree(RIGHT, t->kids[0]->type, t->kids[0], tree(RIGHT, t->kids[0]->type, asgn(caller, idtree(p)), t->kids[0])), t->kids[1]); if (generic((*e)->op) != CALL) t = tree((*e)->op, (*e)->type, t, (*e)->kids[1]); *e = t; } /* bbentry - return tree for _prologue(&afunc, &YYlink)' */ static void bbentry(Symbol yylink, Symbol f) { static Symbol prologue; afunc = genident(STATIC, array(voidptype, 4, 0), GLOBAL); if (prologue == 0) { prologue = mksymbol(EXTERN, "_prologue", ftype(inttype, voidptype)); prologue->defined = 0; } walk(vcall(prologue, voidtype, pointer(idtree(afunc)), pointer(idtree(yylink)), NULL), 0, 0); } /* bbexit - return tree for _epilogue(&afunc)' */ static void bbexit(Symbol yylink, Symbol f, Tree e) { static Symbol epilogue; if (epilogue == 0) { epilogue = mksymbol(EXTERN, "_epilogue", ftype(inttype, voidptype)); epilogue->defined = 0; } walk(vcall(epilogue, voidtype, pointer(idtree(afunc)), NULL), 0, 0); } /* bbfile - add file to list of file names, return its index */ static int bbfile(char *file) { if (file) { List lp; int i = 1; if ((lp = filelist) != NULL) do { lp = lp->link; if (((Symbol)lp->x)->u.c.v.p == file) return i; i++; } while (lp != filelist); filelist = append(mkstr(file), filelist); return i; } return 0; } /* bbfunc - emit function name and src coordinates */ static void bbfunc(Symbol yylink, Symbol f) { Value v; union coordinate u; defglobal(afunc, DATA); defpointer(funclist); defpointer(NULL); defpointer(mkstr(f->name)->u.c.loc); if (IR->little_endian) { u.le.x = f->u.f.pt.x; u.le.y = f->u.f.pt.y; u.le.index = bbfile(f->u.f.pt.file); } else { u.be.x = f->u.f.pt.x; u.be.y = f->u.f.pt.y; u.be.index = bbfile(f->u.f.pt.file); } (*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v)); funclist = afunc; } /* bbincr - build tree to increment execution point at *cp */ static void bbincr(Symbol yycounts, Coordinate *cp, Tree *e) { struct map *mp = maplist->x; Tree t; /* append *cp to source map */ if (mp->size >= NELEMS(mp->u)) { NEW(mp, PERM); mp->size = 0; maplist = append(mp, maplist); } if (IR->little_endian) { mp->u[mp->size].le.x = cp->x; mp->u[mp->size].le.y = cp->y; mp->u[mp->size++].le.index = bbfile(cp->file); } else { mp->u[mp->size].be.x = cp->x; mp->u[mp->size].be.y = cp->y; mp->u[mp->size++].be.index = bbfile(cp->file); } t = incr('+', rvalue((*optree['+'])(ADD, pointer(idtree(yycounts)), consttree(npoints++, inttype))), consttree(1, inttype)); if (*e) *e = tree(RIGHT, (*e)->type, t, *e); else *e = t; } /* bbvars - emit definition for basic block counting data */ static void bbvars(Symbol yylink) { int i, j, n = npoints; Value v; struct map **mp; Symbol coords, files, *p; if (!YYcounts && !yylink) return; if (YYcounts) { if (n <= 0) n = 1; YYcounts->type = array(unsignedtype, n, 0); defglobal(YYcounts, BSS); } files = genident(STATIC, array(charptype, 1, 0), GLOBAL); defglobal(files, LIT); for (p = ltov(&filelist, PERM); *p; p++) defpointer((*p)->u.c.loc); defpointer(NULL); coords = genident(STATIC, array(unsignedtype, n, 0), GLOBAL); defglobal(coords, LIT); for (i = n, mp = ltov(&maplist, PERM); *mp; i -= (*mp)->size, mp++) for (j = 0; j < (*mp)->size; j++) (*IR->defconst)(U, unsignedtype->size, (v.u = (*mp)->u[j].coord, v)); if (i > 0) (*IR->space)(i*coords->type->type->size); defpointer(NULL); defglobal(yylink, DATA); defpointer(NULL); (*IR->defconst)(U, unsignedtype->size, (v.u = n, v)); defpointer(YYcounts); defpointer(coords); defpointer(files); defpointer(funclist); } /* profInit - initialize basic block profiling options */ void prof_init(int argc, char *argv[]) { int i; static int inited; if (inited) return; inited = 1; type_init(argc, argv); if (IR) { for (i = 1; i < argc; i++) if (strncmp(argv[i], "-a", 2) == 0) { if (ncalled == -1 && process(argv[i][2] ? &argv[i][2] : "prof.out") > 0) ncalled = 0; } else if ((strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "-C") == 0) && YYlink == 0) { YYlink = genident(STATIC, array(unsignedtype, 0, 0), GLOBAL); attach((Apply)bbentry, YYlink, &events.entry); attach((Apply)bbexit, YYlink, &events.returns); attach((Apply)bbfunc, YYlink, &events.exit); attach((Apply)bbvars, YYlink, &events.end); if (strcmp(argv[i], "-b") == 0) { YYcounts = genident(STATIC, array(unsignedtype, 0, 0), GLOBAL); maplist = append(allocate(sizeof (struct map), PERM), maplist); ((struct map *)maplist->x)->size = 0; attach((Apply)bbcall, YYcounts, &events.calls); attach((Apply)bbincr, YYcounts, &events.points); } } } } openarena_0.8.8.orig/code/tools/lcc/src/input.c0000644000175000017500000000556611656310262020145 0ustar smcvsmcv#include "c.h" static void pragma(void); static void resynch(void); static int bsize; static unsigned char buffer[MAXLINE+1 + BUFSIZE+1]; unsigned char *cp; /* current input character */ char *file; /* current input file name */ char *firstfile; /* first input file */ unsigned char *limit; /* points to last character + 1 */ char *line; /* current line */ int lineno; /* line number of current line */ void nextline(void) { do { if (cp >= limit) { fillbuf(); if (cp >= limit) cp = limit; if (cp == limit) return; } else { lineno++; for (line = (char *)cp; *cp==' ' || *cp=='\t'; cp++) ; if (*cp == '#') { resynch(); nextline(); } } } while (*cp == '\n' && cp == limit); } void fillbuf(void) { if (bsize == 0) return; if (cp >= limit) cp = &buffer[MAXLINE+1]; else { int n = limit - cp; unsigned char *s = &buffer[MAXLINE+1] - n; assert(s >= buffer); line = (char *)s - ((char *)cp - line); while (cp < limit) *s++ = *cp++; cp = &buffer[MAXLINE+1] - n; } if (feof(stdin)) bsize = 0; else bsize = fread(&buffer[MAXLINE+1], 1, BUFSIZE, stdin); if (bsize < 0) { error("read error\n"); exit(EXIT_FAILURE); } limit = &buffer[MAXLINE+1+bsize]; *limit = '\n'; } void input_init(int argc, char *argv[]) { static int inited; if (inited) return; inited = 1; main_init(argc, argv); limit = cp = &buffer[MAXLINE+1]; bsize = -1; lineno = 0; file = NULL; fillbuf(); if (cp >= limit) cp = limit; nextline(); } /* pragma - handle #pragma ref id... */ static void pragma(void) { if ((t = gettok()) == ID && strcmp(token, "ref") == 0) for (;;) { while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\n' || *cp == 0) break; if ((t = gettok()) == ID && tsym) { tsym->ref++; use(tsym, src); } } } /* resynch - set line number/file name in # n [ "file" ] and #pragma ... */ static void resynch(void) { for (cp++; *cp == ' ' || *cp == '\t'; ) cp++; if (limit - cp < MAXLINE) fillbuf(); if (strncmp((char *)cp, "pragma", 6) == 0) { cp += 6; pragma(); } else if (*cp >= '0' && *cp <= '9') { line: for (lineno = 0; *cp >= '0' && *cp <= '9'; ) lineno = 10*lineno + *cp++ - '0'; lineno--; while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '"') { file = (char *)++cp; while (*cp && *cp != '"' && *cp != '\n') cp++; file = stringn(file, (char *)cp - file); if (*cp == '\n') warning("missing \" in preprocessor line\n"); if (firstfile == 0) firstfile = file; } } else if (strncmp((char *)cp, "line", 4) == 0) { for (cp += 4; *cp == ' ' || *cp == '\t'; ) cp++; if (*cp >= '0' && *cp <= '9') goto line; if (Aflag >= 2) warning("unrecognized control line\n"); } else if (Aflag >= 2 && *cp != '\n') warning("unrecognized control line\n"); while (*cp) if (*cp++ == '\n') { if (cp == limit + 1) nextline(); else break; } } openarena_0.8.8.orig/code/tools/lcc/README.id0000644000175000017500000000022711656310263017314 0ustar smcvsmcv2001-10-31 Timothee Besset updated from the $/source/lcc code modified for portability and use with >= 1.31 mod source release openarena_0.8.8.orig/code/tools/lcc/lburg/0000755000175000017500000000000011720470210017142 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/lburg/lburg.h0000644000175000017500000000374211656310263020445 0ustar smcvsmcv#ifndef BURG_INCLUDED #define BURG_INCLUDED /* iburg.c: */ extern void *alloc(int nbytes); typedef enum { TERM=1, NONTERM } Kind; typedef struct rule *Rule; typedef struct term *Term; struct term { /* terminals: */ char *name; /* terminal name */ Kind kind; /* TERM */ int esn; /* external symbol number */ int arity; /* operator arity */ Term link; /* next terminal in esn order */ Rule rules; /* rules whose pattern starts with term */ }; typedef struct nonterm *Nonterm; struct nonterm { /* nonterminals: */ char *name; /* nonterminal name */ Kind kind; /* NONTERM */ int number; /* identifying number */ int lhscount; /* # times nt appears in a rule lhs */ int reached; /* 1 iff reached from start nonterminal */ Rule rules; /* rules w/nonterminal on lhs */ Rule chain; /* chain rules w/nonterminal on rhs */ Nonterm link; /* next terminal in number order */ }; extern Nonterm nonterm(char *id); extern Term term(char *id, int esn); typedef struct tree *Tree; struct tree { /* tree patterns: */ void *op; /* a terminal or nonterminal */ Tree left, right; /* operands */ int nterms; /* number of terminal nodes in this tree */ }; extern Tree tree(char *op, Tree left, Tree right); struct rule { /* rules: */ Nonterm lhs; /* lefthand side nonterminal */ Tree pattern; /* rule pattern */ int ern; /* external rule number */ int packed; /* packed external rule number */ int cost; /* cost, if a constant */ char *code; /* cost, if an expression */ char *template; /* assembler template */ Rule link; /* next rule in ern order */ Rule next; /* next rule with same pattern root */ Rule chain; /* next chain rule with same rhs */ Rule decode; /* next rule with same lhs */ Rule kids; /* next rule with same _kids pattern */ }; extern Rule rule(char *id, Tree pattern, char *template, char *code); /* gram.y: */ void yyerror(char *fmt, ...); int yyparse(void); void yywarn(char *fmt, ...); extern int errcnt; extern FILE *infp; extern FILE *outfp; #endif openarena_0.8.8.orig/code/tools/lcc/lburg/lburg.10000644000175000017500000001125411656310263020353 0ustar smcvsmcv.TH LBURG 1 "local \- 11/30/94" .\" $Id: lburg.1 145 2001-10-17 21:53:10Z timo $ .SH NAME lburg \- lcc's code-generator generator .SH SYNOPSIS .B lburg [ .I option ]... [ [ .I input ] .I output ] .br .SH DESCRIPTION .PP .I lburg reads an lcc-style BURG specification from .I input and writes a pattern-matching code generator to .IR output . If .I input is `\-' or is omitted, .I lburg reads the standard input; If .I output is `\-' or is omitted, .I lburg writes to the standard output. .PP .I lburg accepts specifications that conform to the following EBNF grammar. Terminals are enclosed in single quotes or are given in uppercase, all other symbols are nonterminals or English phrases, {X} denotes zero or more instances of X, and [X] denotes an optional X. .PP .nf .RS .ft CW spec: `%{' configuration `%}' { dcl } `%%' { rule } [ `%%' C code ] dcl: `%start' nonterm `%term' { ID `=' INT } rule: nonterm `:' tree template [ C expression ] tree: term `(' tree `,' tree `)' term `(' tree `)' term nonterm nonterm: ID template: `"' { any character except double quote } `"' .RE .fi .PP Specifications are structurally similar to .IR yacc 's. Text between `\f(CW%{\fP' and `\f(CW%}\fP' is called the configuration section; there may be several such segments. All are concatenated and copied verbatim into the head of the output. Text after the second `\f(CW%%\fP', if any, is also copied verbatim into the output, at the end. .PP Specifications consist of declarations, a `\f(CW%%\fP' separator, and rules. Input is line-oriented; each declaration and rule must appear on a separate line, and declarations must begin in column 1. Declarations declare terminals \(em the operators in subject trees \(em and associate a unique, positive external symbol number with each one. Nonterminals are declared by their presence on the left side of rules. The \f(CW%start\fP declaration optionally declares a nonterminal as the start symbol. In the grammar above, \f(CWterm\fP and \f(CWnonterm\fP denote identifiers that are terminals and nonterminals. .PP Rules define tree patterns in a fully parenthesized prefix form. Every nonterminal denotes a tree. Each operator has a fixed arity, which is inferred from the rules in which it is used. A chain rule is a rule whose pattern is another nonterminal. If no start symbol is declared, the nonterminal defined by the first rule is used. .PP Each rule ends with an expression that computes the cost of matching that rule; omitted costs default to zero. Costs of chain rules must be constants. .PP The configuration section configures the output for the trees being parsed and the client's environment. As shown, this section must define \f(CWNODEPTR_TYPE\fP to be a visible typedef symbol for a pointer to a node in the subject tree. The labeller invokes \f(CWOP_LABEL(p)\fP, \f(CWLEFT\_CHILD(p)\fP, and \f(CWRIGHT\_CHILD(p)\fP to read the operator and children from the node pointed to by \f(CWp\fP. If the configuration section defines these operations as macros, they are implemented in-line; otherwise, they must be implemented as functions. .PP The matcher computes and stores a single integral state in each node of the subject tree. The configuration section must define a macro \f(CWSTATE_LABEL(p)\fP to access the state field of the node pointed to by \f(CWp\fP. It must be large enough to hold a pointer, and a macro is required because it is used as an lvalue. .PP .SH OPTIONS .TP .BI \-p \ prefix .br .ns .TP .BI \-p prefix Use .I prefix as the disambiquating prefix for visible names and fields. The default is `\f(CW_\fP'. .TP .B \-T Arrange for .sp .nf .ft CW void _trace(NODEPTR_TYPE p, int eruleno, int cost, int bestcost); .sp .fi .ft R to be called at each successful match. \f(CWp\fP identifies the node and \f(CWeruleno\fP identifies the matching rule; the rules are numbered beginning at 1 in the order they appear in the input. \f(CWcost\fP is the cost of the match and \f(CWbestcost\fP is the cost of the best previous match. The current match wins only if \f(CWcost\fP is less than \f(CWbestcost\fP. 32767 represents the infinite cost of no previous match. \f(CW_trace\fP must be declared in the configuration section. .SH "SEE ALSO" .IR lcc (1) .PP C. W. Fraser and D. R. Hanson, .IR A Retargetable C Compiler: Design and Implementation , Benjamin/Cummings, Redwood City, CA, 1995, ISBN 0-8053-1670-1. Chapter 14. .PP C. W. Fraser, D. R. Hanson and T. A. Proebsting, `Engineering a simple, efficient code generator generator,' .I ACM Letters on Programming Languages and Systems .BR 1 , 3 (Sep. 1992), 213-226. .br .SH BUGS Mail bug reports along with the shortest input that exposes them to drh@cs.princeton.edu. openarena_0.8.8.orig/code/tools/lcc/lburg/lburg.c0000644000175000017500000004273511656310263020445 0ustar smcvsmcv#include #include #include #include #include #include #include #include "lburg.h" static char rcsid[] = "lburg.c - faked rcsid"; static char *prefix = ""; static int Tflag = 0; static int ntnumber = 0; static Nonterm start = 0; static Term terms; static Nonterm nts; static Rule rules; static int nrules; static struct block { struct block *link; } *memlist; /* list of allocated blocks */ static char *stringf(char *fmt, ...); static void print(char *fmt, ...); static void ckreach(Nonterm p); static void emitclosure(Nonterm nts); static void emitcost(Tree t, char *v); static void emitdefs(Nonterm nts, int ntnumber); static void emitheader(void); static void emitkids(Rule rules, int nrules); static void emitnts(Rule rules, int nrules); static void emitrecalc(char *pre, Term root, Term kid); static void emitrecord(char *pre, Rule r, char *c, int cost); static void emitrule(Nonterm nts); static void emitlabel(Term terms, Nonterm start, int ntnumber); static void emitstring(Rule rules); static void emitstruct(Nonterm nts, int ntnumber); static void emittest(Tree t, char *v, char *suffix); int main(int argc, char *argv[]) { int c, i; Nonterm p; for (i = 1; i < argc; i++) if (strcmp(argv[i], "-T") == 0) Tflag = 1; else if (strncmp(argv[i], "-p", 2) == 0 && argv[i][2]) prefix = &argv[i][2]; else if (strncmp(argv[i], "-p", 2) == 0 && i + 1 < argc) prefix = argv[++i]; else if (*argv[i] == '-' && argv[i][1]) { yyerror("usage: %s [-T | -p prefix]... [ [ input ] output ] \n", argv[0]); exit(1); } else if (infp == NULL) { if (strcmp(argv[i], "-") == 0) infp = stdin; else if ((infp = fopen(argv[i], "r")) == NULL) { yyerror("%s: can't read `%s'\n", argv[0], argv[i]); exit(1); } } else if (outfp == NULL) { if (strcmp(argv[i], "-") == 0) outfp = stdout; if ((outfp = fopen(argv[i], "w")) == NULL) { yyerror("%s: can't write `%s'\n", argv[0], argv[i]); exit(1); } } if (infp == NULL) infp = stdin; if (outfp == NULL) outfp = stdout; yyparse(); if (start) ckreach(start); for (p = nts; p; p = p->link) { if (p->rules == NULL) yyerror("undefined nonterminal `%s'\n", p->name); if (!p->reached) yyerror("can't reach nonterminal `%s'\n", p->name); } emitheader(); emitdefs(nts, ntnumber); emitstruct(nts, ntnumber); emitnts(rules, nrules); emitstring(rules); emitrule(nts); emitclosure(nts); if (start) emitlabel(terms, start, ntnumber); emitkids(rules, nrules); if (!feof(infp)) while ((c = getc(infp)) != EOF) putc(c, outfp); while (memlist) { /* for purify */ struct block *q = memlist->link; free(memlist); memlist = q; } return errcnt > 0; } /* alloc - allocate nbytes or issue fatal error */ void *alloc(int nbytes) { struct block *p = calloc(1, sizeof *p + nbytes); if (p == NULL) { yyerror("out of memory\n"); exit(1); } p->link = memlist; memlist = p; return p + 1; } /* stringf - format and save a string */ static char *stringf(char *fmt, ...) { va_list ap; char buf[512]; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); return strcpy(alloc(strlen(buf) + 1), buf); } struct entry { union { char *name; struct term t; struct nonterm nt; } sym; struct entry *link; } *table[211]; #define HASHSIZE (sizeof table/sizeof table[0]) /* hash - return hash number for str */ static unsigned hash(char *str) { unsigned h = 0; while (*str) h = (h<<1) + *str++; return h; } /* lookup - lookup symbol name */ static void *lookup(char *name) { struct entry *p = table[hash(name)%HASHSIZE]; for ( ; p; p = p->link) if (strcmp(name, p->sym.name) == 0) return &p->sym; return 0; } /* install - install symbol name */ static void *install(char *name) { struct entry *p = alloc(sizeof *p); int i = hash(name)%HASHSIZE; p->sym.name = name; p->link = table[i]; table[i] = p; return &p->sym; } /* nonterm - create a new terminal id, if necessary */ Nonterm nonterm(char *id) { Nonterm p = lookup(id), *q = &nts; if (p && p->kind == NONTERM) return p; if (p && p->kind == TERM) yyerror("`%s' is a terminal\n", id); p = install(id); p->kind = NONTERM; p->number = ++ntnumber; if (p->number == 1) start = p; while (*q && (*q)->number < p->number) q = &(*q)->link; assert(*q == 0 || (*q)->number != p->number); p->link = *q; *q = p; return p; } /* term - create a new terminal id with external symbol number esn */ Term term(char *id, int esn) { Term p = lookup(id), *q = &terms; if (p) yyerror("redefinition of terminal `%s'\n", id); else p = install(id); p->kind = TERM; p->esn = esn; p->arity = -1; while (*q && (*q)->esn < p->esn) q = &(*q)->link; if (*q && (*q)->esn == p->esn) yyerror("duplicate external symbol number `%s=%d'\n", p->name, p->esn); p->link = *q; *q = p; return p; } /* tree - create & initialize a tree node with the given fields */ Tree tree(char *id, Tree left, Tree right) { Tree t = alloc(sizeof *t); Term p = lookup(id); int arity = 0; if (left && right) arity = 2; else if (left) arity = 1; if (p == NULL && arity > 0) { yyerror("undefined terminal `%s'\n", id); p = term(id, -1); } else if (p == NULL && arity == 0) p = (Term)nonterm(id); else if (p && p->kind == NONTERM && arity > 0) { yyerror("`%s' is a nonterminal\n", id); p = term(id, -1); } if (p->kind == TERM && p->arity == -1) p->arity = arity; if (p->kind == TERM && arity != p->arity) yyerror("inconsistent arity for terminal `%s'\n", id); t->op = p; t->nterms = p->kind == TERM; if ((t->left = left) != NULL) t->nterms += left->nterms; if ((t->right = right) != NULL) t->nterms += right->nterms; return t; } /* rule - create & initialize a rule with the given fields */ Rule rule(char *id, Tree pattern, char *template, char *code) { Rule r = alloc(sizeof *r), *q; Term p = pattern->op; char *end; r->lhs = nonterm(id); r->packed = ++r->lhs->lhscount; for (q = &r->lhs->rules; *q; q = &(*q)->decode) ; *q = r; r->pattern = pattern; r->ern = ++nrules; r->template = template; r->code = code; r->cost = strtol(code, &end, 10); if (*end) { r->cost = -1; r->code = stringf("(%s)", code); } if (p->kind == TERM) { for (q = &p->rules; *q; q = &(*q)->next) ; *q = r; } else if (pattern->left == NULL && pattern->right == NULL) { Nonterm p = pattern->op; r->chain = p->chain; p->chain = r; if (r->cost == -1) yyerror("illegal nonconstant cost `%s'\n", code); } for (q = &rules; *q; q = &(*q)->link) ; r->link = *q; *q = r; return r; } /* print - formatted output */ static void print(char *fmt, ...) { va_list ap; va_start(ap, fmt); for ( ; *fmt; fmt++) if (*fmt == '%') switch (*++fmt) { case 'd': fprintf(outfp, "%d", va_arg(ap, int)); break; case 's': fputs(va_arg(ap, char *), outfp); break; case 'P': fprintf(outfp, "%s_", prefix); break; case 'T': { Tree t = va_arg(ap, Tree); print("%S", t->op); if (t->left && t->right) print("(%T,%T)", t->left, t->right); else if (t->left) print("(%T)", t->left); break; } case 'R': { Rule r = va_arg(ap, Rule); print("%S: %T", r->lhs, r->pattern); break; } case 'S': fputs(va_arg(ap, Term)->name, outfp); break; case '1': case '2': case '3': case '4': case '5': { int n = *fmt - '0'; while (n-- > 0) putc('\t', outfp); break; } default: putc(*fmt, outfp); break; } else putc(*fmt, outfp); va_end(ap); } /* reach - mark all nonterminals in tree t as reachable */ static void reach(Tree t) { Nonterm p = t->op; if (p->kind == NONTERM) if (!p->reached) ckreach(p); if (t->left) reach(t->left); if (t->right) reach(t->right); } /* ckreach - mark all nonterminals reachable from p */ static void ckreach(Nonterm p) { Rule r; p->reached = 1; for (r = p->rules; r; r = r->decode) reach(r->pattern); } /* emitcase - emit one case in function state */ static void emitcase(Term p, int ntnumber) { Rule r; print("%1case %d: /* %S */\n", p->esn, p); switch (p->arity) { case 0: case -1: break; case 1: print("%2%Plabel(LEFT_CHILD(a));\n"); break; case 2: print("%2%Plabel(LEFT_CHILD(a));\n"); print("%2%Plabel(RIGHT_CHILD(a));\n"); break; default: assert(0); } for (r = p->rules; r; r = r->next) { char *indent = "\t\t\0"; switch (p->arity) { case 0: case -1: print("%2/* %R */\n", r); if (r->cost == -1) { print("%2c = %s;\n", r->code); emitrecord("\t\t", r, "c", 0); } else emitrecord("\t\t", r, r->code, 0); break; case 1: if (r->pattern->nterms > 1) { print("%2if (%1/* %R */\n", r); emittest(r->pattern->left, "LEFT_CHILD(a)", " "); print("%2) {\n"); indent = "\t\t\t"; } else print("%2/* %R */\n", r); if (r->pattern->nterms == 2 && r->pattern->left && r->pattern->right == NULL) emitrecalc(indent, r->pattern->op, r->pattern->left->op); print("%sc = ", indent); emitcost(r->pattern->left, "LEFT_CHILD(a)"); print("%s;\n", r->code); emitrecord(indent, r, "c", 0); if (indent[2]) print("%2}\n"); break; case 2: if (r->pattern->nterms > 1) { print("%2if (%1/* %R */\n", r); emittest(r->pattern->left, "LEFT_CHILD(a)", r->pattern->right->nterms ? " && " : " "); emittest(r->pattern->right, "RIGHT_CHILD(a)", " "); print("%2) {\n"); indent = "\t\t\t"; } else print("%2/* %R */\n", r); print("%sc = ", indent); emitcost(r->pattern->left, "LEFT_CHILD(a)"); emitcost(r->pattern->right, "RIGHT_CHILD(a)"); print("%s;\n", r->code); emitrecord(indent, r, "c", 0); if (indent[2]) print("%2}\n"); break; default: assert(0); } } print("%2break;\n"); } /* emitclosure - emit the closure functions */ static void emitclosure(Nonterm nts) { Nonterm p; for (p = nts; p; p = p->link) if (p->chain) print("static void %Pclosure_%S(NODEPTR_TYPE, int);\n", p); print("\n"); for (p = nts; p; p = p->link) if (p->chain) { Rule r; print("static void %Pclosure_%S(NODEPTR_TYPE a, int c) {\n" "%1struct %Pstate *p = STATE_LABEL(a);\n", p); for (r = p->chain; r; r = r->chain) emitrecord("\t", r, "c", r->cost); print("}\n\n"); } } /* emitcost - emit cost computation for tree t */ static void emitcost(Tree t, char *v) { Nonterm p = t->op; if (p->kind == TERM) { if (t->left) emitcost(t->left, stringf("LEFT_CHILD(%s)", v)); if (t->right) emitcost(t->right, stringf("RIGHT_CHILD(%s)", v)); } else print("((struct %Pstate *)(%s->x.state))->cost[%P%S_NT] + ", v, p); } /* emitdefs - emit nonterminal defines and data structures */ static void emitdefs(Nonterm nts, int ntnumber) { Nonterm p; for (p = nts; p; p = p->link) print("#define %P%S_NT %d\n", p, p->number); print("\n"); print("static char *%Pntname[] = {\n%10,\n"); for (p = nts; p; p = p->link) print("%1\"%S\",\n", p); print("%10\n};\n\n"); } /* emitheader - emit initial definitions */ static void emitheader(void) { time_t timer = time(NULL); print("/*\ngenerated at %sby %s\n*/\n", ctime(&timer), rcsid); print("static void %Pkids(NODEPTR_TYPE, int, NODEPTR_TYPE[]);\n"); print("static void %Plabel(NODEPTR_TYPE);\n"); print("static int %Prule(void*, int);\n\n"); } /* computekids - compute paths to kids in tree t */ static char *computekids(Tree t, char *v, char *bp, int *ip) { Term p = t->op; if (p->kind == NONTERM) { sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v); bp += strlen(bp); } else if (p->arity > 0) { bp = computekids(t->left, stringf("LEFT_CHILD(%s)", v), bp, ip); if (p->arity == 2) bp = computekids(t->right, stringf("RIGHT_CHILD(%s)", v), bp, ip); } return bp; } /* emitkids - emit _kids */ static void emitkids(Rule rules, int nrules) { int i; Rule r, *rc = alloc((nrules + 1 + 1)*sizeof *rc); char **str = alloc((nrules + 1 + 1)*sizeof *str); for (i = 0, r = rules; r; r = r->link) { int j = 0; char buf[1024], *bp = buf; *computekids(r->pattern, "p", bp, &j) = 0; for (j = 0; str[j] && strcmp(str[j], buf); j++) ; if (str[j] == NULL) str[j] = strcpy(alloc(strlen(buf) + 1), buf); r->kids = rc[j]; rc[j] = r; } print("static void %Pkids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]) {\n" "%1if (!p)\n%2fatal(\"%Pkids\", \"Null tree\\n\", 0);\n" "%1if (!kids)\n%2fatal(\"%Pkids\", \"Null kids\\n\", 0);\n" "%1switch (eruleno) {\n"); for (i = 0; (r = rc[i]) != NULL; i++) { for ( ; r; r = r->kids) print("%1case %d: /* %R */\n", r->ern, r); print("%s%2break;\n", str[i]); } print("%1default:\n%2fatal(\"%Pkids\", \"Bad rule number %%d\\n\", eruleno);\n%1}\n}\n\n"); } /* emitlabel - emit label function */ static void emitlabel(Term terms, Nonterm start, int ntnumber) { int i; Term p; print("static void %Plabel(NODEPTR_TYPE a) {\n%1int c;\n" "%1struct %Pstate *p;\n\n" "%1if (!a)\n%2fatal(\"%Plabel\", \"Null tree\\n\", 0);\n"); print("%1STATE_LABEL(a) = p = allocate(sizeof *p, FUNC);\n" "%1p->rule._stmt = 0;\n"); for (i = 1; i <= ntnumber; i++) print("%1p->cost[%d] =\n", i); print("%20x7fff;\n%1switch (OP_LABEL(a)) {\n"); for (p = terms; p; p = p->link) emitcase(p, ntnumber); print("%1default:\n" "%2fatal(\"%Plabel\", \"Bad terminal %%d\\n\", OP_LABEL(a));\n%1}\n}\n\n"); } /* computents - fill in bp with _nts vector for tree t */ static char *computents(Tree t, char *bp) { if (t) { Nonterm p = t->op; if (p->kind == NONTERM) { sprintf(bp, "%s_%s_NT, ", prefix, p->name); bp += strlen(bp); } else bp = computents(t->right, computents(t->left, bp)); } return bp; } /* emitnts - emit _nts ragged array */ static void emitnts(Rule rules, int nrules) { Rule r; int i, j, *nts = alloc((nrules + 1)*sizeof *nts); char **str = alloc((nrules + 1)*sizeof *str); for (i = 0, r = rules; r; r = r->link) { char buf[1024]; *computents(r->pattern, buf) = 0; for (j = 0; str[j] && strcmp(str[j], buf); j++) ; if (str[j] == NULL) { print("static short %Pnts_%d[] = { %s0 };\n", j, buf); str[j] = strcpy(alloc(strlen(buf) + 1), buf); } nts[i++] = j; } print("\nstatic short *%Pnts[] = {\n"); for (i = j = 0, r = rules; r; r = r->link) { for ( ; j < r->ern; j++) print("%10,%1/* %d */\n", j); print("%1%Pnts_%d,%1/* %d */\n", nts[i++], j++); } print("};\n\n"); } /* emitrecalc - emit code that tests for recalculation of INDIR?(VREGP) */ static void emitrecalc(char *pre, Term root, Term kid) { if (root->kind == TERM && strncmp(root->name, "INDIR", 5) == 0 && kid->kind == TERM && strcmp(kid->name, "VREGP" ) == 0) { Nonterm p; print("%sif (mayrecalc(a)) {\n", pre); print("%s%1struct %Pstate *q = a->syms[RX]->u.t.cse->x.state;\n", pre); for (p = nts; p; p = p->link) { print("%s%1if (q->cost[%P%S_NT] == 0) {\n", pre, p); print("%s%2p->cost[%P%S_NT] = 0;\n", pre, p); print("%s%2p->rule.%P%S = q->rule.%P%S;\n", pre, p, p); print("%s%1}\n", pre); } print("%s}\n", pre); } } /* emitrecord - emit code that tests for a winning match of rule r */ static void emitrecord(char *pre, Rule r, char *c, int cost) { if (Tflag) print("%s%Ptrace(a, %d, %s + %d, p->cost[%P%S_NT]);\n", pre, r->ern, c, cost, r->lhs); print("%sif (", pre); print("%s + %d < p->cost[%P%S_NT]) {\n" "%s%1p->cost[%P%S_NT] = %s + %d;\n%s%1p->rule.%P%S = %d;\n", c, cost, r->lhs, pre, r->lhs, c, cost, pre, r->lhs, r->packed); if (r->lhs->chain) print("%s%1%Pclosure_%S(a, %s + %d);\n", pre, r->lhs, c, cost); print("%s}\n", pre); } /* emitrule - emit decoding vectors and _rule */ static void emitrule(Nonterm nts) { Nonterm p; for (p = nts; p; p = p->link) { Rule r; print("static short %Pdecode_%S[] = {\n%10,\n", p); for (r = p->rules; r; r = r->decode) print("%1%d,\n", r->ern); print("};\n\n"); } print("static int %Prule(void *state, int goalnt) {\n" "%1if (goalnt < 1 || goalnt > %d)\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n" "%1if (!state)\n%2return 0;\n%1switch (goalnt) {\n", ntnumber); for (p = nts; p; p = p->link) print("%1case %P%S_NT:" "%1return %Pdecode_%S[((struct %Pstate *)state)->rule.%P%S];\n", p, p, p); print("%1default:\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n%2return 0;\n%1}\n}\n\n"); } /* emitstring - emit arrays of templates, instruction flags, and rules */ static void emitstring(Rule rules) { Rule r; print("static char *%Ptemplates[] = {\n"); print("/* 0 */%10,\n"); for (r = rules; r; r = r->link) print("/* %d */%1\"%s\",%1/* %R */\n", r->ern, r->template, r); print("};\n"); print("\nstatic char %Pisinstruction[] = {\n"); print("/* 0 */%10,\n"); for (r = rules; r; r = r->link) { int len = strlen(r->template); print("/* %d */%1%d,%1/* %s */\n", r->ern, len >= 2 && r->template[len-2] == '\\' && r->template[len-1] == 'n', r->template); } print("};\n"); print("\nstatic char *%Pstring[] = {\n"); print("/* 0 */%10,\n"); for (r = rules; r; r = r->link) print("/* %d */%1\"%R\",\n", r->ern, r); print("};\n\n"); } /* emitstruct - emit the definition of the state structure */ static void emitstruct(Nonterm nts, int ntnumber) { print("struct %Pstate {\n%1short cost[%d];\n%1struct {\n", ntnumber + 1); for ( ; nts; nts = nts->link) { int n = 1, m = nts->lhscount; while ((m >>= 1) != 0) n++; print("%2unsigned int %P%S:%d;\n", nts, n); } print("%1} rule;\n};\n\n"); } /* emittest - emit clause for testing a match */ static void emittest(Tree t, char *v, char *suffix) { Term p = t->op; if (p->kind == TERM) { print("%3%s->op == %d%s/* %S */\n", v, p->esn, t->nterms > 1 ? " && " : suffix, p); if (t->left) emittest(t->left, stringf("LEFT_CHILD(%s)", v), t->right && t->right->nterms ? " && " : suffix); if (t->right) emittest(t->right, stringf("RIGHT_CHILD(%s)", v), suffix); } } openarena_0.8.8.orig/code/tools/lcc/lburg/gram.y0000644000175000017500000001022711656310263020275 0ustar smcvsmcv%{ #include #include "lburg.h" static char rcsid[] = "$Id: gram.y 145 2001-10-17 21:53:10Z timo $"; /*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */ static int yylineno = 0; %} %union { int n; char *string; Tree tree; } %term TERMINAL %term START %term PPERCENT %token ID TEMPLATE CODE %token INT %type nonterm cost %type tree %% spec : decls PPERCENT rules { yylineno = 0; } | decls { yylineno = 0; } ; decls : /* lambda */ | decls decl ; decl : TERMINAL blist '\n' | START nonterm '\n' { if (nonterm($2)->number != 1) yyerror("redeclaration of the start symbol\n"); } | '\n' | error '\n' { yyerrok; } ; blist : /* lambda */ | blist ID '=' INT { term($2, $4); } ; rules : /* lambda */ | rules nonterm ':' tree TEMPLATE cost '\n' { rule($2, $4, $5, $6); } | rules '\n' | rules error '\n' { yyerrok; } ; nonterm : ID { nonterm($$ = $1); } ; tree : ID { $$ = tree($1, 0, 0); } | ID '(' tree ')' { $$ = tree($1, $3, 0); } | ID '(' tree ',' tree ')' { $$ = tree($1, $3, $5); } ; cost : CODE { if (*$1 == 0) $$ = "0"; } ; %% #include #include #include #include #include int errcnt = 0; FILE *infp = NULL; FILE *outfp = NULL; static char buf[BUFSIZ], *bp = buf; static int ppercent = 0; static int code = 0; static int get(void) { if (*bp == 0) { bp = buf; *bp = 0; if (fgets(buf, sizeof buf, infp) == NULL) return EOF; yylineno++; while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') { for (;;) { if (fgets(buf, sizeof buf, infp) == NULL) { yywarn("unterminated %{...%}\n"); return EOF; } yylineno++; if (strcmp(buf, "%}\n") == 0) break; fputs(buf, outfp); } if (fgets(buf, sizeof buf, infp) == NULL) return EOF; yylineno++; } } return *bp++; } void yyerror(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (yylineno > 0) fprintf(stderr, "line %d: ", yylineno); vfprintf(stderr, fmt, ap); if (fmt[strlen(fmt)-1] != '\n') fprintf(stderr, "\n"); errcnt++; va_end(ap); } int yylex(void) { int c; if (code) { char *p; bp += strspn(bp, " \t\f"); p = strchr(bp, '\n'); if (p == NULL) p = strchr(bp, '\n'); while (p > bp && isspace(p[-1])) p--; yylval.string = alloc(p - bp + 1); strncpy(yylval.string, bp, p - bp); yylval.string[p - bp] = 0; bp = p; code--; return CODE; } while ((c = get()) != EOF) { switch (c) { case ' ': case '\f': case '\t': continue; case '\n': case '(': case ')': case ',': case ':': case '=': return c; } if (c == '%' && *bp == '%') { bp++; return ppercent++ ? 0 : PPERCENT; } else if (c == '%' && strncmp(bp, "term", 4) == 0 && isspace(bp[4])) { bp += 4; return TERMINAL; } else if (c == '%' && strncmp(bp, "start", 5) == 0 && isspace(bp[5])) { bp += 5; return START; } else if (c == '"') { char *p = strchr(bp, '"'); if (p == NULL) { yyerror("missing \" in assembler template\n"); p = strchr(bp, '\n'); if (p == NULL) p = strchr(bp, '\0'); } assert(p); yylval.string = alloc(p - bp + 1); strncpy(yylval.string, bp, p - bp); yylval.string[p - bp] = 0; bp = *p == '"' ? p + 1 : p; code++; return TEMPLATE; } else if (isdigit(c)) { int n = 0; do { int d = c - '0'; if (n > (INT_MAX - d)/10) yyerror("integer greater than %d\n", INT_MAX); else n = 10*n + d; c = get(); } while (c != EOF && isdigit(c)); bp--; yylval.n = n; return INT; } else if (isalpha(c)) { char *p = bp - 1; while (isalpha(*bp) || isdigit(*bp) || *bp == '_') bp++; yylval.string = alloc(bp - p + 1); strncpy(yylval.string, p, bp - p); yylval.string[bp - p] = 0; return ID; } else if (isprint(c)) yyerror("invalid character `%c'\n", c); else yyerror("invalid character `\\%03o'\n", (unsigned char)c); } return 0; } void yywarn(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (yylineno > 0) fprintf(stderr, "line %d: ", yylineno); fprintf(stderr, "warning: "); vfprintf(stderr, fmt, ap); } openarena_0.8.8.orig/code/tools/lcc/lburg/gram.c0000644000175000017500000004401711656310263020253 0ustar smcvsmcv#if defined(__STDC__) || defined(__cplusplus) #define YYCONST const #define YYPARAMS(x) x #define YYDEFUN(name, arglist, args) name(args) #define YYAND , #define YYPTR void * #else #define YYCONST #define YYPARAMS(x) () #define YYDEFUN(name, arglist, args) name arglist args; #define YYAND ; #define YYPTR char * #endif #ifndef lint YYCONST static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley +Cygnus.28) 01/20/91"; #endif #define YYBYACC 1 #ifndef YYDONT_INCLUDE_STDIO #include #endif //#ifdef __cplusplus TA stdlib.h applies to C too #include /* for malloc/realloc/free */ //#endif #line 2 "lburg/gram.y" #include #include "lburg.h" /*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */ static int yylineno = 0; #line 8 "lburg/gram.y" typedef union { int n; char *string; Tree tree; } YYSTYPE; #line 37 "y.tab.c" #define TERMINAL 257 #define START 258 #define PPERCENT 259 #define ID 260 #define TEMPLATE 261 #define CODE 262 #define INT 263 #define YYERRCODE 256 static YYCONST short yylhs[] = { -1, 0, 0, 4, 4, 6, 6, 6, 6, 7, 7, 5, 5, 5, 5, 1, 3, 3, 3, 2, }; static YYCONST short yylen[] = { 2, 3, 1, 0, 2, 3, 3, 1, 2, 0, 4, 0, 7, 2, 3, 1, 1, 4, 6, 1, }; static YYCONST short yydefred[] = { 3, 0, 0, 0, 9, 0, 11, 7, 4, 8, 0, 15, 0, 0, 0, 5, 6, 0, 13, 0, 0, 14, 0, 10, 0, 0, 0, 0, 0, 19, 0, 17, 0, 12, 0, 18, }; static YYCONST short yydgoto[] = { 1, 12, 30, 25, 2, 13, 8, 10, }; static YYCONST short yysindex[] = { 0, 0, -4, -2, 0, -250, 0, 0, 0, 0, -9, 0, 1, -10, -49, 0, 0, 3, 0, -44, -248, 0, -244, 0, -22, -242, -244, -245, -37, 0, 10, 0, -244, 0, -20, 0, }; static YYCONST short yyrindex[] = { 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static YYCONST short yygindex[] = { 0, 11, 0, -23, 0, 0, 0, 0, }; #define YYTABLESIZE 255 static YYCONST short yytable[] = { 18, 15, 16, 28, 31, 16, 7, 32, 9, 34, 11, 16, 20, 21, 22, 23, 24, 29, 26, 27, 33, 35, 2, 1, 19, 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, 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, 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, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 11, 14, 3, 4, 5, 6, }; static YYCONST short yycheck[] = { 10, 10, 41, 26, 41, 44, 10, 44, 10, 32, 260, 10, 61, 10, 58, 263, 260, 262, 40, 261, 10, 41, 0, 0, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 261, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, -1, 260, 260, 256, 257, 258, 259, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 263 #if YYDEBUG static YYCONST char *YYCONST yyname[] = { "end-of-file",0,0,0,0,0,0,0,0,0,"'\\n'",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,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,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,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, "TERMINAL","START","PPERCENT","ID","TEMPLATE","CODE","INT", }; static YYCONST char *YYCONST yyrule[] = { "$accept : spec", "spec : decls PPERCENT rules", "spec : decls", "decls :", "decls : decls decl", "decl : TERMINAL blist '\\n'", "decl : START nonterm '\\n'", "decl : '\\n'", "decl : error '\\n'", "blist :", "blist : blist ID '=' INT", "rules :", "rules : rules nonterm ':' tree TEMPLATE cost '\\n'", "rules : rules '\\n'", "rules : rules error '\\n'", "nonterm : ID", "tree : ID", "tree : ID '(' tree ')'", "tree : ID '(' tree ',' tree ')'", "cost : CODE", }; #endif #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #ifndef YYINITDEPTH #define YYINITDEPTH 200 #endif #ifdef YYSTACKSIZE #ifndef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #endif #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 500 #define YYMAXDEPTH 500 #endif #endif #ifndef YYMAXSTACKSIZE #define YYMAXSTACKSIZE 10000 #endif int yydebug; int yynerrs; int yyerrflag; int yychar; YYSTYPE yyval; YYSTYPE yylval; static short *yyss; static YYSTYPE *yyvs; static int yystacksize; #define yyfree(x) free(x) extern int yylex(); static YYPTR YYDEFUN (yymalloc, (bytes), unsigned bytes) { YYPTR ptr = (YYPTR) malloc (bytes); if (ptr != 0) return (ptr); yyerror ("yyparse: memory exhausted"); return (0); } static YYPTR YYDEFUN (yyrealloc, (old, bytes), YYPTR old YYAND unsigned bytes) { YYPTR ptr = (YYPTR) realloc (old, bytes); if (ptr != 0) return (ptr); yyerror ("yyparse: memory exhausted"); return (0); } static int #ifdef __GNUC__ inline #endif yygrow () { #if YYDEBUG int old_stacksize = yystacksize; #endif short *new_yyss; YYSTYPE *new_yyvs; if (yystacksize == YYMAXSTACKSIZE) return (1); yystacksize += (yystacksize + 1 ) / 2; if (yystacksize > YYMAXSTACKSIZE) yystacksize = YYMAXSTACKSIZE; #if YYDEBUG if (yydebug) printf("yydebug: growing stack size from %d to %d\n", old_stacksize, yystacksize); #endif new_yyss = (short *) yyrealloc ((char *)yyss, yystacksize * sizeof (short)); if (new_yyss == 0) return (1); new_yyvs = (YYSTYPE *) yyrealloc ((char *)yyvs, yystacksize * sizeof (YYSTYPE)); if (new_yyvs == 0) { yyfree (new_yyss); return (1); } yyss = new_yyss; yyvs = new_yyvs; return (0); } #line 60 "lburg/gram.y" #include #include #include #include #include int errcnt = 0; FILE *infp = NULL; FILE *outfp = NULL; static char buf[BUFSIZ], *bp = buf; static int ppercent = 0; static int code = 0; static int get(void) { if (*bp == 0) { bp = buf; *bp = 0; if (fgets(buf, sizeof buf, infp) == NULL) return EOF; yylineno++; while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') { for (;;) { if (fgets(buf, sizeof buf, infp) == NULL) { yywarn("unterminated %{...%}\n"); return EOF; } yylineno++; if (strcmp(buf, "%}\n") == 0) break; fputs(buf, outfp); } if (fgets(buf, sizeof buf, infp) == NULL) return EOF; yylineno++; } } return *bp++; } void yyerror(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (yylineno > 0) fprintf(stderr, "line %d: ", yylineno); vfprintf(stderr, fmt, ap); if (fmt[strlen(fmt)-1] != '\n') fprintf(stderr, "\n"); errcnt++; va_end(ap); } int yylex(void) { int c; if (code) { char *p; bp += strspn(bp, " \t\f"); p = strchr(bp, '\n'); if (p == NULL) p = strchr(bp, '\n'); while (p > bp && isspace(p[-1])) p--; yylval.string = alloc(p - bp + 1); strncpy(yylval.string, bp, p - bp); yylval.string[p - bp] = 0; bp = p; code--; return CODE; } while ((c = get()) != EOF) { switch (c) { case ' ': case '\f': case '\t': continue; case '\n': case '(': case ')': case ',': case ':': case '=': return c; } if (c == '%' && *bp == '%') { bp++; return ppercent++ ? 0 : PPERCENT; } else if (c == '%' && strncmp(bp, "term", 4) == 0 && isspace(bp[4])) { bp += 4; return TERMINAL; } else if (c == '%' && strncmp(bp, "start", 5) == 0 && isspace(bp[5])) { bp += 5; return START; } else if (c == '"') { char *p = strchr(bp, '"'); if (p == NULL) { yyerror("missing \" in assembler template\n"); p = strchr(bp, '\n'); if (p == NULL) p = strchr(bp, '\0'); } assert(p); yylval.string = alloc(p - bp + 1); strncpy(yylval.string, bp, p - bp); yylval.string[p - bp] = 0; bp = *p == '"' ? p + 1 : p; code++; return TEMPLATE; } else if (isdigit(c)) { int n = 0; do { int d = c - '0'; if (n > (INT_MAX - d)/10) yyerror("integer greater than %d\n", INT_MAX); else n = 10*n + d; c = get(); } while (c != EOF && isdigit(c)); bp--; yylval.n = n; return INT; } else if (isalpha(c)) { char *p = bp - 1; while (isalpha(*bp) || isdigit(*bp) || *bp == '_') bp++; yylval.string = alloc(bp - p + 1); strncpy(yylval.string, p, bp - p); yylval.string[bp - p] = 0; return ID; } else if (isprint(c)) yyerror("invalid character `%c'\n", c); else yyerror("invalid character `\\%03o'\n", (unsigned char)c); } return 0; } void yywarn(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (yylineno > 0) fprintf(stderr, "line %d: ", yylineno); fprintf(stderr, "warning: "); vfprintf(stderr, fmt, ap); } #line 403 "y.tab.c" #define YYABORT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab #if YYDEBUG #ifdef __cplusplus extern "C" char *getenv(); #else extern char *getenv(); #endif #endif int yyparse() { register int yym, yyn, yystate; register YYSTYPE *yyvsp; register short *yyssp; short *yysse; #if YYDEBUG register YYCONST char *yys; if (yys = getenv("YYDEBUG")) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == 0) { yyss = (short *) yymalloc (YYSTACKSIZE * sizeof (short)); if (yyss == 0) goto yyabort; yyvs = (YYSTYPE *) yymalloc (YYSTACKSIZE * sizeof (YYSTYPE)); if (yyvs == 0) { yyfree (yyss); goto yyabort; } yystacksize = YYSTACKSIZE; } yysse = yyss + yystacksize - 1; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; goto yyloop; yypush_lex: yyval = yylval; yystate = yytable[yyn]; yypush: if (yyssp >= yysse) { int depth = yyssp - yyss; if (yygrow() != 0) goto yyoverflow; yysse = yyss + yystacksize -1; yyssp = depth + yyss; yyvsp = depth + yyvs; } *++yyssp = yystate; *++yyvsp = yyval; yyloop: if ((yyn = yydefred[yystate])) goto yyreduce; yyn = yysindex[yystate]; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("yydebug: state %d, reading %d (%s)\n", yystate, yychar, yys); } #endif } if (yyn != 0 && ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE)) && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("yydebug: state %d, shifting to state %d\n", yystate, yytable[yyn]); #endif if (yyerrflag > 0) --yyerrflag; yychar = (-1); goto yypush_lex; } yyn = yyrindex[yystate]; if (yyn != 0 && ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE)) && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #ifdef lint goto yynewerror; yynewerror: #endif yyerror("syntax error"); #ifdef lint goto yyerrlab; yyerrlab: #endif ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { yyn = yysindex[*yyssp]; if (yyn != 0 && ((yyn += YYERRCODE), ((unsigned)yyn <= (unsigned)YYTABLESIZE)) && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("yydebug: state %d, error recovery shifting\ to state %d\n", *yyssp, yytable[yyn]); #endif goto yypush_lex; } else { #if YYDEBUG if (yydebug) printf("yydebug: error recovery discarding state %d\n", *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("yydebug: state %d, error recovery discards token %d (%s)\n", yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("yydebug: state %d, reducing by rule %d (%s)\n", yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; switch (yyn) { case 1: #line 22 "lburg/gram.y" { yylineno = 0; } break; case 2: #line 23 "lburg/gram.y" { yylineno = 0; } break; case 6: #line 31 "lburg/gram.y" { if (nonterm(yyvsp[-1].string)->number != 1) yyerror("redeclaration of the start symbol\n"); } break; case 8: #line 36 "lburg/gram.y" { yyerrok; } break; case 10: #line 40 "lburg/gram.y" { term(yyvsp[-2].string, yyvsp[0].n); } break; case 12: #line 44 "lburg/gram.y" { rule(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-2].string, yyvsp[-1].string); } break; case 14: #line 46 "lburg/gram.y" { yyerrok; } break; case 15: #line 49 "lburg/gram.y" { nonterm(yyval.string = yyvsp[0].string); } break; case 16: #line 52 "lburg/gram.y" { yyval.tree = tree(yyvsp[0].string, 0, 0); } break; case 17: #line 53 "lburg/gram.y" { yyval.tree = tree(yyvsp[-3].string, yyvsp[-1].tree, 0); } break; case 18: #line 54 "lburg/gram.y" { yyval.tree = tree(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-1].tree); } break; case 19: #line 57 "lburg/gram.y" { if (*yyvsp[0].string == 0) yyval.string = "0"; } break; #line 630 "y.tab.c" } yyssp -= yym; yyvsp -= yym; yym = yylhs[yyn]; yystate = *yyssp; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("yydebug: after reduction, shifting from state 0 to\ state %d\n", YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("yydebug: state %d, reading %d (%s)\n", YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } yyn = yygindex[yym]; if (yyn != 0 && ((yyn += yystate), ((unsigned)yyn <= (unsigned)YYTABLESIZE)) && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("yydebug: after reduction, shifting from state %d \ to state %d\n", *yyssp, yystate); #endif goto yypush; yyoverflow: yyerror("yacc stack overflow"); yyabort: return (1); yyaccept: return (0); } openarena_0.8.8.orig/code/tools/lcc/README0000644000175000017500000000155611656310263016727 0ustar smcvsmcvThis hierarchy is the distribution for lcc version 4.1. lcc version 3.x is described in the book "A Retargetable C Compiler: Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1). There are significant differences between 3.x and 4.x, most notably in the intermediate code. doc/4.html summarizes the differences. VERSION 4.1 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION. LOG describes the changes since the last release. CPYRIGHT describes the conditions under you can use, copy, modify, and distribute lcc or works derived from lcc. doc/install.html is an HTML file that gives a complete description of the distribution and installation instructions. Chris Fraser / cwfraser@microsoft.com David Hanson / drh@microsoft.com $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ openarena_0.8.8.orig/code/tools/lcc/COPYRIGHT0000644000175000017500000000554711656310263017346 0ustar smcvsmcvThe authors of this software are Christopher W. Fraser and David R. Hanson. Copyright (c) 1991,1992,1993,1994,1995,1996,1997,1998 by AT&T, Christopher W. Fraser, and David R. Hanson. All Rights Reserved. Permission to use, copy, modify, and distribute this software for any purpose, subject to the provisions described below, without fee is hereby granted, provided that this entire notice is included in all copies of any software that is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. lcc is not public-domain software, shareware, and it is not protected by a `copyleft' agreement, like the code from the Free Software Foundation. lcc is available free for your personal research and instructional use under the `fair use' provisions of the copyright law. You may, however, redistribute lcc in whole or in part provided you acknowledge its source and include this CPYRIGHT file. You may, for example, include the distribution in a CDROM of free software, provided you charge only for the media, or mirror the distribution files at your site. You may not sell lcc or any product derived from it in which it is a significant part of the value of the product. Using the lcc front end to build a C syntax checker is an example of this kind of product. You may use parts of lcc in products as long as you charge for only those components that are entirely your own and you acknowledge the use of lcc clearly in all product documentation and distribution media. You must state clearly that your product uses or is based on parts of lcc and that lcc is available free of charge. You must also request that bug reports on your product be reported to you. Using the lcc front end to build a C compiler for the Motorola 88000 chip and charging for and distributing only the 88000 code generator is an example of this kind of product. Using parts of lcc in other products is more problematic. For example, using parts of lcc in a C++ compiler could save substantial time and effort and therefore contribute significantly to the profitability of the product. This kind of use, or any use where others stand to make a profit from what is primarily our work, requires a license agreement with Addison-Wesley. Per-copy and unlimited use licenses are available; for more information, contact J. Carter Shanklin Addison Wesley Longman, Inc. 2725 Sand Hill Rd. Menlo Park, CA 94025 650/854-0300 x2478 FAX: 650/614-2930 jcs@awl.com ----- Chris Fraser / cwfraser@microsoft.com David Hanson / drh@microsoft.com $Revision: 145 $ $Date: 2001-10-17 16:53:10 -0500 (Wed, 17 Oct 2001) $ openarena_0.8.8.orig/code/tools/lcc/etc/0000755000175000017500000000000011720470210016602 5ustar smcvsmcvopenarena_0.8.8.orig/code/tools/lcc/etc/bytecode.c0000644000175000017500000000315711656310263020563 0ustar smcvsmcv/* quake3 bytecode target */ #include #include #include "../../../qcommon/q_platform.h" #ifdef _WIN32 #define BINEXT ".exe" #else #define BINEXT "" #endif char *suffixes[] = { ".c", ".i", ".asm", ".o", ".out", 0 }; char inputs[256] = ""; char *cpp[] = { "q3cpp" BINEXT, "-D__STDC__=1", "-D__STRICT_ANSI__", "-D__signed__=signed", "-DQ3_VM", "$1", "$2", "$3", 0 }; char *include[] = { 0 }; char *com[] = { "q3rcc" BINEXT, "-target=bytecode", "$1", "$2", "$3", 0 }; char *ld[] = { 0 }; char *as[] = { 0 }; extern char *concat(char *, char *); /* =============== UpdatePaths Updates the paths to q3cpp and q3rcc based on the directory that contains q3lcc =============== */ void UpdatePaths( const char *lccBinary ) { char basepath[ 1024 ]; char *p; strncpy( basepath, lccBinary, 1024 ); p = strrchr( basepath, PATH_SEP ); if( p ) { *( p + 1 ) = '\0'; cpp[ 0 ] = concat( basepath, "q3cpp" BINEXT ); com[ 0 ] = concat( basepath, "q3rcc" BINEXT ); } } int option(char *arg) { if (strncmp(arg, "-lccdir=", 8) == 0) { cpp[0] = concat(&arg[8], "/q3cpp" BINEXT); include[0] = concat("-I", concat(&arg[8], "/include")); com[0] = concat(&arg[8], "/q3rcc" BINEXT); } else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-pg") == 0) { fprintf( stderr, "no profiling supported, %s ignored.\n", arg); } else if (strcmp(arg, "-b") == 0) ; else if (strcmp(arg, "-g") == 0) fprintf( stderr, "no debugging supported, %s ignored.\n", arg); else if (strncmp(arg, "-ld=", 4) == 0 || strcmp(arg, "-static") == 0) { fprintf( stderr, "no linking supported, %s ignored.\n", arg); } else return 0; return 1; } openarena_0.8.8.orig/code/tools/lcc/etc/lcc.c0000644000175000017500000004622511656310263017531 0ustar smcvsmcv/* * lcc [ option ]... [ file | -llib ]... * front end for the ANSI C compiler */ static char rcsid[] = "Id: dummy rcsid"; #include #include #include #include #include #include #include #include #ifndef TEMPDIR #define TEMPDIR "/tmp" #endif typedef struct list *List; struct list { /* circular list nodes: */ char *str; /* option or file name */ List link; /* next list element */ }; static void *alloc(int); static List append(char *,List); extern char *basename(char *); static int callsys(char *[]); extern char *concat(char *, char *); static int compile(char *, char *); static void compose(char *[], List, List, List); static void error(char *, char *); static char *exists(char *); static char *first(char *); static int filename(char *, char *); static List find(char *, List); static void help(void); static void initinputs(void); static void interrupt(int); static void opt(char *); static List path2list(const char *); extern int main(int, char *[]); extern char *replace(const char *, int, int); static void rm(List); extern char *strsave(const char *); extern char *stringf(const char *, ...); extern int suffix(char *, char *[], int); extern char *tempname(char *); #ifndef __sun extern int getpid(void); #endif extern char *cpp[], *include[], *com[], *as[],*ld[], inputs[], *suffixes[]; extern int option(char *); static int errcnt; /* number of errors */ static int Eflag; /* -E specified */ static int Sflag = 1; /* -S specified */ //for Q3 we always generate asm static int cflag; /* -c specified */ static int verbose; /* incremented for each -v */ static List llist[2]; /* loader files, flags */ static List alist; /* assembler flags */ static List clist; /* compiler flags */ static List plist; /* preprocessor flags */ static List ilist; /* list of additional includes from LCCINPUTS */ static List rmlist; /* list of files to remove */ static char *outfile; /* ld output file or -[cS] object file */ static int ac; /* argument count */ static char **av; /* argument vector */ char *tempdir = TEMPDIR; /* directory for temporary files */ static char *progname; static List lccinputs; /* list of input directories */ extern void UpdatePaths( const char *lccBinary ); int main(int argc, char *argv[]) { int i, j, nf; progname = argv[0]; UpdatePaths( progname ); ac = argc + 50; av = alloc(ac*sizeof(char *)); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, interrupt); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, interrupt); #ifdef SIGHUP if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, interrupt); #endif if (getenv("TMP")) tempdir = getenv("TMP"); else if (getenv("TEMP")) tempdir = getenv("TEMP"); else if (getenv("TMPDIR")) tempdir = getenv("TMPDIR"); assert(tempdir); i = strlen(tempdir); for (; (i > 0 && tempdir[i-1] == '/') || tempdir[i-1] == '\\'; i--) tempdir[i-1] = '\0'; if (argc <= 1) { help(); exit(0); } plist = append("-D__LCC__", 0); initinputs(); if (getenv("LCCDIR")) option(stringf("-lccdir=%s", getenv("LCCDIR"))); for (nf = 0, i = j = 1; i < argc; i++) { if (strcmp(argv[i], "-o") == 0) { if (++i < argc) { if (suffix(argv[i], suffixes, 2) >= 0) { error("-o would overwrite %s", argv[i]); exit(8); } outfile = argv[i]; continue; } else { error("unrecognized option `%s'", argv[i-1]); exit(8); } } else if (strcmp(argv[i], "-target") == 0) { if (argv[i+1] && *argv[i+1] != '-') i++; continue; } else if (*argv[i] == '-' && argv[i][1] != 'l') { opt(argv[i]); continue; } else if (*argv[i] != '-' && suffix(argv[i], suffixes, 3) >= 0) nf++; argv[j++] = argv[i]; } if ((cflag || Sflag) && outfile && nf != 1) { fprintf(stderr, "%s: -o %s ignored\n", progname, outfile); outfile = 0; } argv[j] = 0; for (i = 0; include[i]; i++) plist = append(include[i], plist); if (ilist) { List b = ilist; do { b = b->link; plist = append(b->str, plist); } while (b != ilist); } ilist = 0; for (i = 1; argv[i]; i++) if (*argv[i] == '-') opt(argv[i]); else { char *name = exists(argv[i]); if (name) { if (strcmp(name, argv[i]) != 0 || (nf > 1 && suffix(name, suffixes, 3) >= 0)) fprintf(stderr, "%s:\n", name); filename(name, 0); } else error("can't find `%s'", argv[i]); } if (errcnt == 0 && !Eflag && !Sflag && !cflag && llist[1]) { compose(ld, llist[0], llist[1], append(outfile ? outfile : concat("a", first(suffixes[4])), 0)); if (callsys(av)) errcnt++; } rm(rmlist); return errcnt ? EXIT_FAILURE : EXIT_SUCCESS; } /* alloc - allocate n bytes or die */ static void *alloc(int n) { static char *avail, *limit; n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1); if (n >= limit - avail) { avail = malloc(n + 4*1024); assert(avail); limit = avail + n + 4*1024; } avail += n; return avail - n; } /* append - append a node with string str onto list, return new list */ static List append(char *str, List list) { List p = alloc(sizeof *p); p->str = str; if (list) { p->link = list->link; list->link = p; } else p->link = p; return p; } /* basename - return base name for name, e.g. /usr/drh/foo.c => foo */ char *basename(char *name) { char *s, *b, *t = 0; for (b = s = name; *s; s++) if (*s == '/' || *s == '\\') { b = s + 1; t = 0; } else if (*s == '.') t = s; s = strsave(b); if (t) s[t-b] = 0; return s; } #ifdef WIN32 #include #else #define _P_WAIT 0 #ifndef __sun extern int fork(void); #endif extern int wait(int *); static int _spawnvp(int mode, const char *cmdname, char *argv[]) { int pid, n, status; switch (pid = fork()) { case -1: fprintf(stderr, "%s: no more processes\n", progname); return 100; case 0: // TTimo removing hardcoded paths, searching in $PATH execvp(cmdname, argv); fprintf(stderr, "%s: ", progname); perror(cmdname); fflush(stdout); exit(100); } while ((n = wait(&status)) != pid && n != -1) ; if (n == -1) status = -1; if (status&0377) { fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname); status |= 0400; } return (status>>8)&0377; } #endif /* callsys - execute the command described by av[0...], return status */ static int callsys(char **av) { int i, status = 0; static char **argv; static int argc; char *executable; for (i = 0; av[i] != NULL; i++) ; if (i + 1 > argc) { argc = i + 1; if (argv == NULL) argv = malloc(argc*sizeof *argv); else argv = realloc(argv, argc*sizeof *argv); assert(argv); } for (i = 0; status == 0 && av[i] != NULL; ) { int j = 0; char *s = NULL; for ( ; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++) argv[j++] = av[i]; if (s != NULL) { if (s > av[i]) argv[j++] = stringf("%.*s", s - av[i], av[i]); if (s[1] != '\0') av[i] = s + 1; else i++; } argv[j] = NULL; executable = strsave( argv[0] ); argv[0] = stringf( "\"%s\"", argv[0] ); if (verbose > 0) { int k; fprintf(stderr, "%s", argv[0]); for (k = 1; argv[k] != NULL; k++) fprintf(stderr, " %s", argv[k]); fprintf(stderr, "\n"); } if (verbose < 2) #ifndef WIN32 status = _spawnvp(_P_WAIT, executable, argv); #else status = _spawnvp(_P_WAIT, executable, (const char* const*)argv); #endif if (status == -1) { fprintf(stderr, "%s: ", progname); perror(argv[0]); } } return status; } /* concat - return concatenation of strings s1 and s2 */ char *concat(char *s1, char *s2) { int n = strlen(s1); char *s = alloc(n + strlen(s2) + 1); strcpy(s, s1); strcpy(s + n, s2); return s; } /* compile - compile src into dst, return status */ static int compile(char *src, char *dst) { compose(com, clist, append(src, 0), append(dst, 0)); return callsys(av); } /* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */ static void compose(char *cmd[], List a, List b, List c) { int i, j; List lists[3]; lists[0] = a; lists[1] = b; lists[2] = c; for (i = j = 0; cmd[i]; i++) { char *s = strchr(cmd[i], '$'); if (s && isdigit(s[1])) { int k = s[1] - '0'; assert(k >=1 && k <= 3); if ((b = lists[k-1])) { b = b->link; av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1); strncpy(av[j], cmd[i], s - cmd[i]); av[j][s-cmd[i]] = '\0'; strcat(av[j], b->str); strcat(av[j++], s + 2); while (b != lists[k-1]) { b = b->link; assert(j < ac); av[j++] = b->str; }; } } else if (*cmd[i]) { assert(j < ac); av[j++] = cmd[i]; } } av[j] = NULL; } /* error - issue error msg according to fmt, bump error count */ static void error(char *fmt, char *msg) { fprintf(stderr, "%s: ", progname); fprintf(stderr, fmt, msg); fprintf(stderr, "\n"); errcnt++; } /* exists - if `name' readable return its path name or return null */ static char *exists(char *name) { List b; if ( (name[0] == '/' || name[0] == '\\' || name[2] == ':') && access(name, 4) == 0) return name; if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':') && (b = lccinputs)) do { b = b->link; if (b->str[0]) { char buf[1024]; sprintf(buf, "%s/%s", b->str, name); if (access(buf, 4) == 0) return strsave(buf); } else if (access(name, 4) == 0) return name; } while (b != lccinputs); if (verbose > 1) return name; return 0; } /* first - return first component in semicolon separated list */ static char *first(char *list) { char *s = strchr(list, ';'); if (s) { char buf[1024]; strncpy(buf, list, s-list); buf[s-list] = '\0'; return strsave(buf); } else return list; } /* filename - process file name argument `name', return status */ static int filename(char *name, char *base) { int status = 0; static char *stemp, *itemp; if (base == 0) base = basename(name); switch (suffix(name, suffixes, 4)) { case 0: /* C source files */ compose(cpp, plist, append(name, 0), 0); if (Eflag) { status = callsys(av); break; } if (itemp == NULL) itemp = tempname(first(suffixes[1])); compose(cpp, plist, append(name, 0), append(itemp, 0)); status = callsys(av); if (status == 0) return filename(itemp, base); break; case 1: /* preprocessed source files */ if (Eflag) break; if (Sflag) status = compile(name, outfile ? outfile : concat(base, first(suffixes[2]))); else if ((status = compile(name, stemp?stemp:(stemp=tempname(first(suffixes[2]))))) == 0) return filename(stemp, base); break; case 2: /* assembly language files */ if (Eflag) break; if (!Sflag) { char *ofile; if (cflag && outfile) ofile = outfile; else if (cflag) ofile = concat(base, first(suffixes[3])); else ofile = tempname(first(suffixes[3])); compose(as, alist, append(name, 0), append(ofile, 0)); status = callsys(av); if (!find(ofile, llist[1])) llist[1] = append(ofile, llist[1]); } break; case 3: /* object files */ if (!find(name, llist[1])) llist[1] = append(name, llist[1]); break; default: if (Eflag) { compose(cpp, plist, append(name, 0), 0); status = callsys(av); } llist[1] = append(name, llist[1]); break; } if (status) errcnt++; return status; } /* find - find 1st occurrence of str in list, return list node or 0 */ static List find(char *str, List list) { List b; if ((b = list)) do { if (strcmp(str, b->str) == 0) return b; } while ((b = b->link) != list); return 0; } /* help - print help message */ static void help(void) { static char *msgs[] = { "", " [ option | file ]...\n", " except for -l, options are processed left-to-right before files\n", " unrecognized options are taken to be linker options\n", "-A warn about nonANSI usage; 2nd -A warns more\n", "-b emit expression-level profiling code; see bprint(1)\n", #ifdef sparc "-Bstatic -Bdynamic specify static or dynamic libraries\n", #endif "-Bdir/ use the compiler named `dir/rcc'\n", "-c compile only\n", "-dn set switch statement density to `n'\n", "-Dname -Dname=def define the preprocessor symbol `name'\n", "-E run only the preprocessor on the named C programs and unsuffixed files\n", "-g produce symbol table information for debuggers\n", "-help or -? print this message\n", "-Idir add `dir' to the beginning of the list of #include directories\n", "-lx search library `x'\n", "-N do not search the standard directories for #include files\n", "-n emit code to check for dereferencing zero pointers\n", "-O is ignored\n", "-o file leave the output in `file'\n", "-P print ANSI-style declarations for globals\n", "-p -pg emit profiling code; see prof(1) and gprof(1)\n", "-S compile to assembly language\n", #ifdef linux "-static specify static libraries (default is dynamic)\n", #endif "-t -tname emit function tracing calls to printf or to `name'\n", "-target name is ignored\n", "-tempdir=dir place temporary files in `dir/'", "\n" "-Uname undefine the preprocessor symbol `name'\n", "-v show commands as they are executed; 2nd -v suppresses execution\n", "-w suppress warnings\n", "-Woarg specify system-specific `arg'\n", "-W[pfal]arg pass `arg' to the preprocessor, compiler, assembler, or linker\n", 0 }; int i; char *s; msgs[0] = progname; for (i = 0; msgs[i]; i++) { fprintf(stderr, "%s", msgs[i]); if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir) fprintf(stderr, "; default=%s", tempdir); } #define xx(v) if ((s = getenv(#v))) fprintf(stderr, #v "=%s\n", s) xx(LCCINPUTS); xx(LCCDIR); #undef xx } /* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */ static void initinputs(void) { char *s = getenv("LCCINPUTS"); List b; if (s == 0 || (s = inputs)[0] == 0) s = "."; if (s) { lccinputs = path2list(s); if ((b = lccinputs)) do { b = b->link; if (strcmp(b->str, ".") != 0) { ilist = append(concat("-I", b->str), ilist); if (strstr(com[1], "win32") == NULL) llist[0] = append(concat("-L", b->str), llist[0]); } else b->str = ""; } while (b != lccinputs); } } /* interrupt - catch interrupt signals */ static void interrupt(int n) { rm(rmlist); exit(n = 100); } /* opt - process option in arg */ static void opt(char *arg) { switch (arg[1]) { /* multi-character options */ case 'W': /* -Wxarg */ if (arg[2] && arg[3]) switch (arg[2]) { case 'o': if (option(&arg[3])) return; break; case 'p': plist = append(&arg[3], plist); return; case 'f': if (strcmp(&arg[3], "-C") || option("-b")) { clist = append(&arg[3], clist); return; } break; /* and fall thru */ case 'a': alist = append(&arg[3], alist); return; case 'l': llist[0] = append(&arg[3], llist[0]); return; } fprintf(stderr, "%s: %s ignored\n", progname, arg); return; case 'd': /* -dn */ arg[1] = 's'; clist = append(arg, clist); return; case 't': /* -t -tname -tempdir=dir */ if (strncmp(arg, "-tempdir=", 9) == 0) tempdir = arg + 9; else clist = append(arg, clist); return; case 'p': /* -p -pg */ if (option(arg)) clist = append(arg, clist); else fprintf(stderr, "%s: %s ignored\n", progname, arg); return; case 'D': /* -Dname -Dname=def */ case 'U': /* -Uname */ case 'I': /* -Idir */ plist = append(arg, plist); return; case 'B': /* -Bdir -Bstatic -Bdynamic */ #ifdef sparc if (strcmp(arg, "-Bstatic") == 0 || strcmp(arg, "-Bdynamic") == 0) llist[1] = append(arg, llist[1]); else #endif { static char *path; if (path) error("-B overwrites earlier option", 0); path = arg + 2; if (strstr(com[1], "win32") != NULL) com[0] = concat(replace(path, '/', '\\'), concat("rcc", first(suffixes[4]))); else com[0] = concat(path, "rcc"); if (path[0] == 0) error("missing directory in -B option", 0); } return; case 'h': if (strcmp(arg, "-help") == 0) { static int printed = 0; case '?': if (!printed) help(); printed = 1; return; } #ifdef linux case 's': if (strcmp(arg,"-static") == 0) { if (!option(arg)) fprintf(stderr, "%s: %s ignored\n", progname, arg); return; } #endif } if (arg[2] == 0) switch (arg[1]) { /* single-character options */ case 'S': Sflag++; return; case 'O': fprintf(stderr, "%s: %s ignored\n", progname, arg); return; case 'A': case 'n': case 'w': case 'P': clist = append(arg, clist); return; case 'g': case 'b': if (option(arg)) clist = append(arg[1] == 'g' ? "-g2" : arg, clist); else fprintf(stderr, "%s: %s ignored\n", progname, arg); return; case 'G': if (option(arg)) { clist = append("-g3", clist); llist[0] = append("-N", llist[0]); } else fprintf(stderr, "%s: %s ignored\n", progname, arg); return; case 'E': Eflag++; return; case 'c': cflag++; return; case 'N': if (strcmp(basename(cpp[0]), "gcc-cpp") == 0) plist = append("-nostdinc", plist); include[0] = 0; ilist = 0; return; case 'v': if (verbose++ == 0) { if (strcmp(basename(cpp[0]), "gcc-cpp") == 0) plist = append(arg, plist); clist = append(arg, clist); fprintf(stderr, "%s %s\n", progname, rcsid); } return; } if (cflag || Sflag || Eflag) fprintf(stderr, "%s: %s ignored\n", progname, arg); else llist[1] = append(arg, llist[1]); } /* path2list - convert a colon- or semicolon-separated list to a list */ static List path2list(const char *path) { List list = NULL; char sep = ':'; if (path == NULL) return NULL; if (strchr(path, ';')) sep = ';'; while (*path) { char *p, buf[512]; if ((p = strchr(path, sep))) { assert(p - path < sizeof buf); strncpy(buf, path, p - path); buf[p-path] = '\0'; } else { assert(strlen(path) < sizeof buf); strcpy(buf, path); } if (!find(buf, list)) list = append(strsave(buf), list); if (p == 0) break; path = p + 1; } return list; } /* replace - copy str, then replace occurrences of from with to, return the copy */ char *replace(const char *str, int from, int to) { char *s = strsave(str), *p = s; for ( ; (p = strchr(p, from)) != NULL; p++) *p = to; return s; } /* rm - remove files in list */ static void rm(List list) { if (list) { List b = list; if (verbose) fprintf(stderr, "rm"); do { if (verbose) fprintf(stderr, " %s", b->str); if (verbose < 2) remove(b->str); } while ((b = b->link) != list); if (verbose) fprintf(stderr, "\n"); } } /* strsave - return a saved copy of string str */ char *strsave(const char *str) { return strcpy(alloc(strlen(str)+1), str); } /* stringf - format and return a string */ char *stringf(const char *fmt, ...) { char buf[1024]; va_list ap; int n; va_start(ap, fmt); n = vsprintf(buf, fmt, ap); va_end(ap); return strsave(buf); } /* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */ int suffix(char *name, char *tails[], int n) { int i, len = strlen(name); for (i = 0; i < n; i++) { char *s = tails[i], *t; for ( ; (t = strchr(s, ';')); s = t + 1) { int m = t - s; if (len > m && strncmp(&name[len-m], s, m) == 0) return i; } if (*s) { int m = strlen(s); if (len > m && strncmp(&name[len-m], s, m) == 0) return i; } } return -1; } /* tempname - generate a temporary file name in tempdir with given suffix */ char *tempname(char *suffix) { static int n; char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix); if (strstr(com[1], "win32") != NULL) name = replace(name, '/', '\\'); rmlist = append(name, rmlist); return name; } openarena_0.8.8.orig/code/botlib/0000755000175000017500000000000011720470203015403 5ustar smcvsmcvopenarena_0.8.8.orig/code/botlib/be_ai_move.h0000644000175000017500000001363411656310264017660 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_move.h * * desc: movement AI * * $Archive: /source/code/botlib/be_ai_move.h $ * *****************************************************************************/ //movement types #define MOVE_WALK 1 #define MOVE_CROUCH 2 #define MOVE_JUMP 4 #define MOVE_GRAPPLE 8 #define MOVE_ROCKETJUMP 16 #define MOVE_BFGJUMP 32 //move flags #define MFL_BARRIERJUMP 1 //bot is performing a barrier jump #define MFL_ONGROUND 2 //bot is in the ground #define MFL_SWIMMING 4 //bot is swimming #define MFL_AGAINSTLADDER 8 //bot is against a ladder #define MFL_WATERJUMP 16 //bot is waterjumping #define MFL_TELEPORTED 32 //bot is being teleported #define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple #define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook #define MFL_GRAPPLERESET 256 //bot has reset the grapple #define MFL_WALK 512 //bot should walk slowly // move result flags #define MOVERESULT_MOVEMENTVIEW 1 //bot uses view for movement #define MOVERESULT_SWIMVIEW 2 //bot uses view for swimming #define MOVERESULT_WAITING 4 //bot is waiting for something #define MOVERESULT_MOVEMENTVIEWSET 8 //bot has set the view in movement code #define MOVERESULT_MOVEMENTWEAPON 16 //bot uses weapon for movement #define MOVERESULT_ONTOPOFOBSTACLE 32 //bot is ontop of obstacle #define MOVERESULT_ONTOPOF_FUNCBOB 64 //bot is ontop of a func_bobbing #define MOVERESULT_ONTOPOF_ELEVATOR 128 //bot is ontop of an elevator (func_plat) #define MOVERESULT_BLOCKEDBYAVOIDSPOT 256 //bot is blocked by an avoid spot // #define MAX_AVOIDREACH 1 #define MAX_AVOIDSPOTS 32 // avoid spot types #define AVOID_CLEAR 0 //clear all avoid spots #define AVOID_ALWAYS 1 //avoid always #define AVOID_DONTBLOCK 2 //never totally block // restult types #define RESULTTYPE_ELEVATORUP 1 //elevator is up #define RESULTTYPE_WAITFORFUNCBOBBING 2 //waiting for func bobbing to arrive #define RESULTTYPE_BADGRAPPLEPATH 4 //grapple path is obstructed #define RESULTTYPE_INSOLIDAREA 8 //stuck in solid area, this is bad //structure used to initialize the movement state //the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate typedef struct bot_initmove_s { vec3_t origin; //origin of the bot vec3_t velocity; //velocity of the bot vec3_t viewoffset; //view offset int entitynum; //entity number of the bot int client; //client number of the bot float thinktime; //time the bot thinks int presencetype; //presencetype of the bot vec3_t viewangles; //view angles of the bot int or_moveflags; //values ored to the movement flags } bot_initmove_t; //NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set typedef struct bot_moveresult_s { int failure; //true if movement failed all together int type; //failure or blocked type int blocked; //true if blocked by an entity int blockentity; //entity blocking the bot int traveltype; //last executed travel type int flags; //result flags int weapon; //weapon used for movement vec3_t movedir; //movement direction vec3_t ideal_viewangles; //ideal viewangles for the movement } bot_moveresult_t; #define bot_moveresult_t_cleared(x) bot_moveresult_t (x) = {0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}} typedef struct bot_avoidspot_s { vec3_t origin; float radius; int type; } bot_avoidspot_t; //resets the whole move state void BotResetMoveState(int movestate); //moves the bot to the given goal void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags); //moves the bot in the specified direction using the specified type of movement int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type); //reset avoid reachability void BotResetAvoidReach(int movestate); //resets the last avoid reachability void BotResetLastAvoidReach(int movestate); //returns a reachability area if the origin is in one int BotReachabilityArea(vec3_t origin, int client); //view target based on movement int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target); //predict the position of a player based on movement towards a goal int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); //returns the handle of a newly allocated movestate int BotAllocMoveState(void); //frees the movestate with the given handle void BotFreeMoveState(int handle); //initialize movement state before performing any movement void BotInitMoveState(int handle, bot_initmove_t *initmove); //add a spot to avoid (if type == AVOID_CLEAR all spots are removed) void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type); //must be called every map change void BotSetBrushModelTypes(void); //setup movement AI int BotSetupMoveAI(void); //shutdown movement AI void BotShutdownMoveAI(void); openarena_0.8.8.orig/code/botlib/be_ai_char.h0000644000175000017500000000366711656310264017634 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_char.h * * desc: bot characters * * $Archive: /source/code/botlib/be_ai_char.h $ * *****************************************************************************/ //loads a bot character from a file int BotLoadCharacter(char *charfile, float skill); //frees a bot character void BotFreeCharacter(int character); //returns a float characteristic float Characteristic_Float(int character, int index); //returns a bounded float characteristic float Characteristic_BFloat(int character, int index, float min, float max); //returns an integer characteristic int Characteristic_Integer(int character, int index); //returns a bounded integer characteristic int Characteristic_BInteger(int character, int index, int min, int max); //returns a string characteristic void Characteristic_String(int character, int index, char *buf, int size); //free cached bot characters void BotShutdownCharacters(void); openarena_0.8.8.orig/code/botlib/l_script.h0000644000175000017500000002005111656310264017401 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_script.h * * desc: lexicographical parser * * $Archive: /source/code/botlib/l_script.h $ * *****************************************************************************/ //undef if binary numbers of the form 0b... or 0B... are not allowed #define BINARYNUMBERS //undef if not using the token.intvalue and token.floatvalue #define NUMBERVALUE //use dollar sign also as punctuation #define DOLLAR //maximum token length #define MAX_TOKEN 1024 #if defined(BSPC) && !defined(QDECL) #define QDECL #endif //script flags #define SCFL_NOERRORS 0x0001 #define SCFL_NOWARNINGS 0x0002 #define SCFL_NOSTRINGWHITESPACES 0x0004 #define SCFL_NOSTRINGESCAPECHARS 0x0008 #define SCFL_PRIMITIVE 0x0010 #define SCFL_NOBINARYNUMBERS 0x0020 #define SCFL_NONUMBERVALUES 0x0040 //token types #define TT_STRING 1 // string #define TT_LITERAL 2 // literal #define TT_NUMBER 3 // number #define TT_NAME 4 // name #define TT_PUNCTUATION 5 // punctuation //string sub type //--------------- // the length of the string //literal sub type //---------------- // the ASCII code of the literal //number sub type //--------------- #define TT_DECIMAL 0x0008 // decimal number #define TT_HEX 0x0100 // hexadecimal number #define TT_OCTAL 0x0200 // octal number #ifdef BINARYNUMBERS #define TT_BINARY 0x0400 // binary number #endif //BINARYNUMBERS #define TT_FLOAT 0x0800 // floating point number #define TT_INTEGER 0x1000 // integer number #define TT_LONG 0x2000 // long number #define TT_UNSIGNED 0x4000 // unsigned number //punctuation sub type //-------------------- #define P_RSHIFT_ASSIGN 1 #define P_LSHIFT_ASSIGN 2 #define P_PARMS 3 #define P_PRECOMPMERGE 4 #define P_LOGIC_AND 5 #define P_LOGIC_OR 6 #define P_LOGIC_GEQ 7 #define P_LOGIC_LEQ 8 #define P_LOGIC_EQ 9 #define P_LOGIC_UNEQ 10 #define P_MUL_ASSIGN 11 #define P_DIV_ASSIGN 12 #define P_MOD_ASSIGN 13 #define P_ADD_ASSIGN 14 #define P_SUB_ASSIGN 15 #define P_INC 16 #define P_DEC 17 #define P_BIN_AND_ASSIGN 18 #define P_BIN_OR_ASSIGN 19 #define P_BIN_XOR_ASSIGN 20 #define P_RSHIFT 21 #define P_LSHIFT 22 #define P_POINTERREF 23 #define P_CPP1 24 #define P_CPP2 25 #define P_MUL 26 #define P_DIV 27 #define P_MOD 28 #define P_ADD 29 #define P_SUB 30 #define P_ASSIGN 31 #define P_BIN_AND 32 #define P_BIN_OR 33 #define P_BIN_XOR 34 #define P_BIN_NOT 35 #define P_LOGIC_NOT 36 #define P_LOGIC_GREATER 37 #define P_LOGIC_LESS 38 #define P_REF 39 #define P_COMMA 40 #define P_SEMICOLON 41 #define P_COLON 42 #define P_QUESTIONMARK 43 #define P_PARENTHESESOPEN 44 #define P_PARENTHESESCLOSE 45 #define P_BRACEOPEN 46 #define P_BRACECLOSE 47 #define P_SQBRACKETOPEN 48 #define P_SQBRACKETCLOSE 49 #define P_BACKSLASH 50 #define P_PRECOMP 51 #define P_DOLLAR 52 //name sub type //------------- // the length of the name //punctuation typedef struct punctuation_s { char *p; //punctuation character(s) int n; //punctuation indication struct punctuation_s *next; //next punctuation } punctuation_t; //token typedef struct token_s { char string[MAX_TOKEN]; //available token int type; //last read token type int subtype; //last read token sub type #ifdef NUMBERVALUE unsigned long int intvalue; //integer value float floatvalue; //floating point value #endif //NUMBERVALUE char *whitespace_p; //start of white space before token char *endwhitespace_p; //start of white space before token int line; //line the token was on int linescrossed; //lines crossed in white space struct token_s *next; //next token in chain } token_t; //script file typedef struct script_s { char filename[1024]; //file name of the script char *buffer; //buffer containing the script char *script_p; //current pointer in the script char *end_p; //pointer to the end of the script char *lastscript_p; //script pointer before reading token char *whitespace_p; //begin of the white space char *endwhitespace_p; //end of the white space int length; //length of the script in bytes int line; //current line in script int lastline; //line before reading token int tokenavailable; //set by UnreadLastToken int flags; //several script flags punctuation_t *punctuations; //the punctuations used in the script punctuation_t **punctuationtable; token_t token; //available token struct script_s *next; //next script in a chain } script_t; //read a token from the script int PS_ReadToken(script_t *script, token_t *token); //expect a certain token int PS_ExpectTokenString(script_t *script, char *string); //expect a certain token type int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token); //expect a token int PS_ExpectAnyToken(script_t *script, token_t *token); //returns true when the token is available int PS_CheckTokenString(script_t *script, char *string); //returns true an reads the token when a token with the given type is available int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token); //skip tokens until the given token string is read int PS_SkipUntilString(script_t *script, char *string); //unread the last token read from the script void PS_UnreadLastToken(script_t *script); //unread the given token void PS_UnreadToken(script_t *script, token_t *token); //returns the next character of the read white space, returns NULL if none char PS_NextWhiteSpaceChar(script_t *script); //remove any leading and trailing double quotes from the token void StripDoubleQuotes(char *string); //remove any leading and trailing single quotes from the token void StripSingleQuotes(char *string); //read a possible signed integer signed long int ReadSignedInt(script_t *script); //read a possible signed floating point number float ReadSignedFloat(script_t *script); //set an array with punctuations, NULL restores default C/C++ set void SetScriptPunctuations(script_t *script, punctuation_t *p); //set script flags void SetScriptFlags(script_t *script, int flags); //get script flags int GetScriptFlags(script_t *script); //reset a script void ResetScript(script_t *script); //returns true if at the end of the script int EndOfScript(script_t *script); //returns a pointer to the punctuation with the given number char *PunctuationFromNum(script_t *script, int num); //load a script from the given file at the given offset with the given length script_t *LoadScriptFile(const char *filename); //load a script from the given memory with the given length script_t *LoadScriptMemory(char *ptr, int length, char *name); //free a script void FreeScript(script_t *script); //set the base folder to load files from void PS_SetBaseFolder(char *path); //print a script error with filename and line number void QDECL ScriptError(script_t *script, char *str, ...); //print a script warning with filename and line number void QDECL ScriptWarning(script_t *script, char *str, ...); openarena_0.8.8.orig/code/botlib/be_ai_goal.h0000644000175000017500000001142411656310264017627 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_goal.h * * desc: goal AI * * $Archive: /source/code/botlib/be_ai_goal.h $ * *****************************************************************************/ #define MAX_AVOIDGOALS 256 #define MAX_GOALSTACK 8 #define GFL_NONE 0 #define GFL_ITEM 1 #define GFL_ROAM 2 #define GFL_DROPPED 4 //a bot goal typedef struct bot_goal_s { vec3_t origin; //origin of the goal int areanum; //area number of the goal vec3_t mins, maxs; //mins and maxs of the goal int entitynum; //number of the goal entity int number; //goal number int flags; //goal flags int iteminfo; //item information } bot_goal_t; //reset the whole goal state, but keep the item weights void BotResetGoalState(int goalstate); //reset avoid goals void BotResetAvoidGoals(int goalstate); //remove the goal with the given number from the avoid goals void BotRemoveFromAvoidGoals(int goalstate, int number); //push a goal onto the goal stack void BotPushGoal(int goalstate, bot_goal_t *goal); //pop a goal from the goal stack void BotPopGoal(int goalstate); //empty the bot's goal stack void BotEmptyGoalStack(int goalstate); //dump the avoid goals void BotDumpAvoidGoals(int goalstate); //dump the goal stack void BotDumpGoalStack(int goalstate); //get the name name of the goal with the given number void BotGoalName(int number, char *name, int size); //get the top goal from the stack int BotGetTopGoal(int goalstate, bot_goal_t *goal); //get the second goal on the stack int BotGetSecondGoal(int goalstate, bot_goal_t *goal); //choose the best long term goal item for the bot int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags); //choose the best nearby goal item for the bot //the item may not be further away from the current bot position than maxtime //also the travel time from the nearby goal towards the long term goal may not //be larger than the travel time towards the long term goal from the current bot position int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, bot_goal_t *ltg, float maxtime); //returns true if the bot touches the goal int BotTouchingGoal(vec3_t origin, bot_goal_t *goal); //returns true if the goal should be visible but isn't int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal); //search for a goal for the given classname, the index can be used //as a start point for the search when multiple goals are available with that same classname int BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal); //get the next camp spot in the map int BotGetNextCampSpotGoal(int num, bot_goal_t *goal); //get the map location with the given name int BotGetMapLocationGoal(char *name, bot_goal_t *goal); //returns the avoid goal time float BotAvoidGoalTime(int goalstate, int number); //set the avoid goal time void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime); //initializes the items in the level void BotInitLevelItems(void); //regularly update dynamic entity items (dropped weapons, flags etc.) void BotUpdateEntityItems(void); //interbreed the goal fuzzy logic void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child); //save the goal fuzzy logic to disk void BotSaveGoalFuzzyLogic(int goalstate, char *filename); //mutate the goal fuzzy logic void BotMutateGoalFuzzyLogic(int goalstate, float range); //loads item weights for the bot int BotLoadItemWeights(int goalstate, char *filename); //frees the item weights of the bot void BotFreeItemWeights(int goalstate); //returns the handle of a newly allocated goal state int BotAllocGoalState(int client); //free the given goal state void BotFreeGoalState(int handle); //setup the goal AI int BotSetupGoalAI(void); //shut down the goal AI void BotShutdownGoalAI(void); openarena_0.8.8.orig/code/botlib/l_libvar.h0000644000175000017500000000454711656310264017370 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_libvar.h * * desc: botlib vars * * $Archive: /source/code/botlib/l_libvar.h $ * *****************************************************************************/ //library variable typedef struct libvar_s { char *name; char *string; int flags; qboolean modified; // set each time the cvar is changed float value; struct libvar_s *next; } libvar_t; //removes all library variables void LibVarDeAllocAll(void); //gets the library variable with the given name libvar_t *LibVarGet(char *var_name); //gets the string of the library variable with the given name char *LibVarGetString(char *var_name); //gets the value of the library variable with the given name float LibVarGetValue(char *var_name); //creates the library variable if not existing already and returns it libvar_t *LibVar(char *var_name, char *value); //creates the library variable if not existing already and returns the value float LibVarValue(char *var_name, char *value); //creates the library variable if not existing already and returns the value string char *LibVarString(char *var_name, char *value); //sets the library variable void LibVarSet(char *var_name, char *value); //returns true if the library variable has been modified qboolean LibVarChanged(char *var_name); //sets the library variable to unmodified void LibVarSetNotModified(char *var_name); openarena_0.8.8.orig/code/botlib/be_aas_funcs.h0000644000175000017500000000307111656310264020175 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_funcs.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_funcs.h $ * *****************************************************************************/ #ifndef BSPCINCLUDE #include "be_aas_main.h" #include "be_aas_entity.h" #include "be_aas_sample.h" #include "be_aas_cluster.h" #include "be_aas_reach.h" #include "be_aas_route.h" #include "be_aas_routealt.h" #include "be_aas_debug.h" #include "be_aas_file.h" #include "be_aas_optimize.h" #include "be_aas_bsp.h" #include "be_aas_move.h" #endif //BSPCINCLUDE openarena_0.8.8.orig/code/botlib/be_ai_weap.h0000644000175000017500000000624011656310264017641 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_weap.h * * desc: weapon AI * * $Archive: /source/code/botlib/be_ai_weap.h $ * *****************************************************************************/ //projectile flags #define PFL_WINDOWDAMAGE 1 //projectile damages through window #define PFL_RETURN 2 //set when projectile returns to owner //weapon flags #define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event //damage types #define DAMAGETYPE_IMPACT 1 //damage on impact #define DAMAGETYPE_RADIAL 2 //radial damage #define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile typedef struct projectileinfo_s { char name[MAX_STRINGFIELD]; char model[MAX_STRINGFIELD]; int flags; float gravity; int damage; float radius; int visdamage; int damagetype; int healthinc; float push; float detonation; float bounce; float bouncefric; float bouncestop; } projectileinfo_t; typedef struct weaponinfo_s { int valid; //true if the weapon info is valid int number; //number of the weapon char name[MAX_STRINGFIELD]; char model[MAX_STRINGFIELD]; int level; int weaponindex; int flags; char projectile[MAX_STRINGFIELD]; int numprojectiles; float hspread; float vspread; float speed; float acceleration; vec3_t recoil; vec3_t offset; vec3_t angleoffset; float extrazvelocity; int ammoamount; int ammoindex; float activate; float reload; float spinup; float spindown; projectileinfo_t proj; //pointer to the used projectile } weaponinfo_t; //setup the weapon AI int BotSetupWeaponAI(void); //shut down the weapon AI void BotShutdownWeaponAI(void); //returns the best weapon to fight with int BotChooseBestFightWeapon(int weaponstate, int *inventory); //returns the information of the current weapon void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo); //loads the weapon weights int BotLoadWeaponWeights(int weaponstate, char *filename); //returns a handle to a newly allocated weapon state int BotAllocWeaponState(void); //frees the weapon state void BotFreeWeaponState(int weaponstate); //resets the whole weapon state void BotResetWeaponState(int weaponstate); openarena_0.8.8.orig/code/botlib/be_aas_bsp.h0000644000175000017500000000631311656310264017645 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_bsp.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_bsp.h $ * *****************************************************************************/ #ifdef AASINTERN //loads the given BSP file int AAS_LoadBSPFile(void); //dump the loaded BSP data void AAS_DumpBSPData(void); //unlink the given entity from the bsp tree leaves void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves); //link the given entity to the bsp tree leaves of the given model bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum); //calculates collision with given entity qboolean AAS_EntityCollision(int entnum, vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, int contentmask, bsp_trace_t *trace); //for debugging void AAS_PrintFreeBSPLinks(char *str); // #endif //AASINTERN #define MAX_EPAIRKEY 128 //trace through the world bsp_trace_t AAS_Trace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); //returns the contents at the given point int AAS_PointContents(vec3_t point); //returns true when p2 is in the PVS of p1 qboolean AAS_inPVS(vec3_t p1, vec3_t p2); //returns true when p2 is in the PHS of p1 qboolean AAS_inPHS(vec3_t p1, vec3_t p2); //returns true if the given areas are connected qboolean AAS_AreasConnected(int area1, int area2); //creates a list with entities totally or partly within the given box int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount); //gets the mins, maxs and origin of a BSP model void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); //handle to the next bsp entity int AAS_NextBSPEntity(int ent); //return the value of the BSP epair key int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); //get a vector for the BSP epair key int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); //get a float for the BSP epair key int AAS_FloatForBSPEpairKey(int ent, char *key, float *value); //get an integer for the BSP epair key int AAS_IntForBSPEpairKey(int ent, char *key, int *value); openarena_0.8.8.orig/code/botlib/l_struct.h0000644000175000017500000000472311656310264017431 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_struct.h * * desc: structure reading/writing * * $Archive: /source/code/botlib/l_struct.h $ * *****************************************************************************/ #define MAX_STRINGFIELD 80 //field types #define FT_CHAR 1 // char #define FT_INT 2 // int #define FT_FLOAT 3 // float #define FT_STRING 4 // char [MAX_STRINGFIELD] #define FT_STRUCT 6 // struct (sub structure) //type only mask #define FT_TYPE 0x00FF // only type, clear subtype //sub types #define FT_ARRAY 0x0100 // array of type #define FT_BOUNDED 0x0200 // bounded value #define FT_UNSIGNED 0x0400 //structure field definition typedef struct fielddef_s { char *name; //name of the field int offset; //offset in the structure int type; //type of the field //type specific fields int maxarray; //maximum array size float floatmin, floatmax; //float min and max struct structdef_s *substruct; //sub structure } fielddef_t; //structure definition typedef struct structdef_s { int size; fielddef_t *fields; } structdef_t; //read a structure from a script int ReadStructure(source_t *source, structdef_t *def, char *structure); //write a structure to a file int WriteStructure(FILE *fp, structdef_t *def, char *structure); //writes indents int WriteIndent(FILE *fp, int indent); //writes a float without traling zeros int WriteFloat(FILE *fp, float value); openarena_0.8.8.orig/code/botlib/be_aas_optimize.h0000644000175000017500000000236111656310264020720 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_optimize.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_optimize.h $ * *****************************************************************************/ void AAS_Optimize(void); openarena_0.8.8.orig/code/botlib/linux-i386.mak0000644000175000017500000000321411656310264017733 0ustar smcvsmcv# # Makefile for Gladiator Bot library: gladiator.so # Intended for gcc/Linux # ARCH=i386 CC=gcc BASE_CFLAGS=-Dstricmp=strcasecmp #use these cflags to optimize it CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ -malign-jumps=2 -malign-functions=2 #use these when debugging #CFLAGS=$(BASE_CFLAGS) -g LDFLAGS=-ldl -lm SHLIBEXT=so SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< ############################################################################# # SETUP AND BUILD # GLADIATOR BOT ############################################################################# .c.o: $(DO_CC) GAME_OBJS = \ be_aas_bsphl.o\ be_aas_bspq2.o\ be_aas_cluster.o\ be_aas_debug.o\ be_aas_entity.o\ be_aas_file.o\ be_aas_light.o\ be_aas_main.o\ be_aas_move.o\ be_aas_optimize.o\ be_aas_reach.o\ be_aas_route.o\ be_aas_routealt.o\ be_aas_sample.o\ be_aas_sound.o\ be_ai2_dmq2.o\ be_ai2_dmhl.o\ be_ai2_dmnet.o\ be_ai2_main.o\ be_ai_char.o\ be_ai_chat.o\ be_ai_goal.o\ be_ai_load.o\ be_ai_move.o\ be_ai_weap.o\ be_ai_weight.o\ be_ea.o\ be_interface.o\ l_crc.o\ l_libvar.o\ l_log.o\ l_memory.o\ l_precomp.o\ l_script.o\ l_struct.o\ l_utils.o\ q_shared.o glad$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) ############################################################################# # MISC ############################################################################# clean: -rm -f $(GAME_OBJS) depend: gcc -MM $(GAME_OBJS:.o=.c) install: cp gladiator.so .. # # From "make depend" # openarena_0.8.8.orig/code/botlib/be_ea.h0000644000175000017500000000437011656310264016623 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ea.h * * desc: elementary actions * * $Archive: /source/code/botlib/be_ea.h $ * *****************************************************************************/ //ClientCommand elementary actions void EA_Say(int client, char *str); void EA_SayTeam(int client, char *str); void EA_Command(int client, char *command ); void EA_Action(int client, int action); void EA_Crouch(int client); void EA_Walk(int client); void EA_MoveUp(int client); void EA_MoveDown(int client); void EA_MoveForward(int client); void EA_MoveBack(int client); void EA_MoveLeft(int client); void EA_MoveRight(int client); void EA_Attack(int client); void EA_Respawn(int client); void EA_Talk(int client); void EA_Gesture(int client); void EA_Use(int client); //regular elementary actions void EA_SelectWeapon(int client, int weapon); void EA_Jump(int client); void EA_DelayedJump(int client); void EA_Move(int client, vec3_t dir, float speed); void EA_View(int client, vec3_t viewangles); //send regular input to the server void EA_EndRegular(int client, float thinktime); void EA_GetInput(int client, float thinktime, bot_input_t *input); void EA_ResetInput(int client); //setup and shutdown routines int EA_Setup(void); void EA_Shutdown(void); openarena_0.8.8.orig/code/botlib/botlib.h0000644000175000017500000005377511656310264017060 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: botlib.h * * desc: bot AI library * * $Archive: /source/code/game/botai.h $ * *****************************************************************************/ #define BOTLIB_API_VERSION 2 struct aas_clientmove_s; struct aas_entityinfo_s; struct aas_areainfo_s; struct aas_altroutegoal_s; struct aas_predictroute_s; struct bot_consolemessage_s; struct bot_match_s; struct bot_goal_s; struct bot_moveresult_s; struct bot_initmove_s; struct weaponinfo_s; #define BOTFILESBASEFOLDER "botfiles" //debug line colors #define LINECOLOR_NONE -1 #define LINECOLOR_RED 1//0xf2f2f0f0L #define LINECOLOR_GREEN 2//0xd0d1d2d3L #define LINECOLOR_BLUE 3//0xf3f3f1f1L #define LINECOLOR_YELLOW 4//0xdcdddedfL #define LINECOLOR_ORANGE 5//0xe0e1e2e3L //Print types #define PRT_MESSAGE 1 #define PRT_WARNING 2 #define PRT_ERROR 3 #define PRT_FATAL 4 #define PRT_EXIT 5 //console message types #define CMS_NORMAL 0 #define CMS_CHAT 1 //botlib error codes #define BLERR_NOERROR 0 //no error #define BLERR_LIBRARYNOTSETUP 1 //library not setup #define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number #define BLERR_NOAASFILE 3 //no AAS file available #define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file #define BLERR_WRONGAASFILEID 5 //incorrect AAS file id #define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version #define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump #define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats #define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights #define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config #define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights #define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config //action flags #define ACTION_ATTACK 0x0000001 #define ACTION_USE 0x0000002 #define ACTION_RESPAWN 0x0000008 #define ACTION_JUMP 0x0000010 #define ACTION_MOVEUP 0x0000020 #define ACTION_CROUCH 0x0000080 #define ACTION_MOVEDOWN 0x0000100 #define ACTION_MOVEFORWARD 0x0000200 #define ACTION_MOVEBACK 0x0000800 #define ACTION_MOVELEFT 0x0001000 #define ACTION_MOVERIGHT 0x0002000 #define ACTION_DELAYEDJUMP 0x0008000 #define ACTION_TALK 0x0010000 #define ACTION_GESTURE 0x0020000 #define ACTION_WALK 0x0080000 #define ACTION_AFFIRMATIVE 0x0100000 #define ACTION_NEGATIVE 0x0200000 #define ACTION_GETFLAG 0x0800000 #define ACTION_GUARDBASE 0x1000000 #define ACTION_PATROL 0x2000000 #define ACTION_FOLLOWME 0x8000000 //the bot input, will be converted to an usercmd_t typedef struct bot_input_s { float thinktime; //time since last output (in seconds) vec3_t dir; //movement direction float speed; //speed in the range [0, 400] vec3_t viewangles; //the view angles int actionflags; //one of the ACTION_? flags int weapon; //weapon to use } bot_input_t; #ifndef BSPTRACE #define BSPTRACE //bsp_trace_t hit surface typedef struct bsp_surface_s { char name[16]; int flags; int value; } bsp_surface_t; //remove the bsp_trace_s structure definition l8r on //a trace is returned when a box is swept through the world typedef struct bsp_trace_s { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position cplane_t plane; // surface normal at impact float exp_dist; // expanded plane distance int sidenum; // number of the brush side hit bsp_surface_t surface; // the hit point surface int contents; // contents on other side of surface hit int ent; // number of entity hit } bsp_trace_t; #endif // BSPTRACE //entity state typedef struct bot_entitystate_s { int type; // entity type int flags; // entity flags vec3_t origin; // origin of the entity vec3_t angles; // angles of the model vec3_t old_origin; // for lerping vec3_t mins; // bounding box minimums vec3_t maxs; // bounding box maximums int groundent; // ground entity int solid; // solid type int modelindex; // model used int modelindex2; // weapons, CTF flags, etc int frame; // model frame number int event; // impulse events -- muzzle flashes, footsteps, etc int eventParm; // even parameter int powerups; // bit flags int weapon; // determines weapon and flash model, etc int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT } bot_entitystate_t; //bot AI library exported functions typedef struct botlib_import_s { //print messages from the bot library void (QDECL *Print)(int type, char *fmt, ...); //trace a bbox through the world void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); //trace a bbox against a specific entity void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask); //retrieve the contents at the given point int (*PointContents)(vec3_t point); //check if the point is in potential visible sight int (*inPVS)(vec3_t p1, vec3_t p2); //retrieve the BSP entity data lump char *(*BSPEntityData)(void); // void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); //send a bot client command void (*BotClientCommand)(int client, char *command); //memory allocation void *(*GetMemory)(int size); // allocate from Zone void (*FreeMemory)(void *ptr); // free memory from Zone int (*AvailableMemory)(void); // available Zone memory void *(*HunkAlloc)(int size); // allocate from hunk //file system access int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode ); int (*FS_Read)( void *buffer, int len, fileHandle_t f ); int (*FS_Write)( const void *buffer, int len, fileHandle_t f ); void (*FS_FCloseFile)( fileHandle_t f ); int (*FS_Seek)( fileHandle_t f, long offset, int origin ); //debug visualisation stuff int (*DebugLineCreate)(void); void (*DebugLineDelete)(int line); void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color); // int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points); void (*DebugPolygonDelete)(int id); } botlib_import_t; typedef struct aas_export_s { //----------------------------------- // be_aas_entity.h //----------------------------------- void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info); //----------------------------------- // be_aas_main.h //----------------------------------- int (*AAS_Initialized)(void); void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs); float (*AAS_Time)(void); //-------------------------------------------- // be_aas_sample.c //-------------------------------------------- int (*AAS_PointAreaNum)(vec3_t point); int (*AAS_PointReachabilityAreaIndex)( vec3_t point ); int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info ); //-------------------------------------------- // be_aas_bspq3.c //-------------------------------------------- int (*AAS_PointContents)(vec3_t point); int (*AAS_NextBSPEntity)(int ent); int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size); int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v); int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value); int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value); //-------------------------------------------- // be_aas_reach.c //-------------------------------------------- int (*AAS_AreaReachability)(int areanum); //-------------------------------------------- // be_aas_route.c //-------------------------------------------- int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags); int (*AAS_EnableRoutingArea)(int areanum, int enable); int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum); //-------------------------------------------- // be_aas_altroute.c //-------------------------------------------- int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals, int type); //-------------------------------------------- // be_aas_move.c //-------------------------------------------- int (*AAS_Swimming)(vec3_t origin); int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize); } aas_export_t; typedef struct ea_export_s { //ClientCommand elementary actions void (*EA_Command)(int client, char *command ); void (*EA_Say)(int client, char *str); void (*EA_SayTeam)(int client, char *str); // void (*EA_Action)(int client, int action); void (*EA_Gesture)(int client); void (*EA_Talk)(int client); void (*EA_Attack)(int client); void (*EA_Use)(int client); void (*EA_Respawn)(int client); void (*EA_MoveUp)(int client); void (*EA_MoveDown)(int client); void (*EA_MoveForward)(int client); void (*EA_MoveBack)(int client); void (*EA_MoveLeft)(int client); void (*EA_MoveRight)(int client); void (*EA_Crouch)(int client); void (*EA_SelectWeapon)(int client, int weapon); void (*EA_Jump)(int client); void (*EA_DelayedJump)(int client); void (*EA_Move)(int client, vec3_t dir, float speed); void (*EA_View)(int client, vec3_t viewangles); //send regular input to the server void (*EA_EndRegular)(int client, float thinktime); void (*EA_GetInput)(int client, float thinktime, bot_input_t *input); void (*EA_ResetInput)(int client); } ea_export_t; typedef struct ai_export_s { //----------------------------------- // be_ai_char.h //----------------------------------- int (*BotLoadCharacter)(char *charfile, float skill); void (*BotFreeCharacter)(int character); float (*Characteristic_Float)(int character, int index); float (*Characteristic_BFloat)(int character, int index, float min, float max); int (*Characteristic_Integer)(int character, int index); int (*Characteristic_BInteger)(int character, int index, int min, int max); void (*Characteristic_String)(int character, int index, char *buf, int size); //----------------------------------- // be_ai_chat.h //----------------------------------- int (*BotAllocChatState)(void); void (*BotFreeChatState)(int handle); void (*BotQueueConsoleMessage)(int chatstate, int type, char *message); void (*BotRemoveConsoleMessage)(int chatstate, int handle); int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm); int (*BotNumConsoleMessages)(int chatstate); void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); int (*BotNumInitialChats)(int chatstate, char *type); int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); int (*BotChatLength)(int chatstate); void (*BotEnterChat)(int chatstate, int client, int sendto); void (*BotGetChatMessage)(int chatstate, char *buf, int size); int (*StringContains)(char *str1, char *str2, int casesensitive); int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context); void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size); void (*UnifyWhiteSpaces)(char *string); void (*BotReplaceSynonyms)(char *string, unsigned long int context); int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname); void (*BotSetChatGender)(int chatstate, int gender); void (*BotSetChatName)(int chatstate, char *name, int client); //----------------------------------- // be_ai_goal.h //----------------------------------- void (*BotResetGoalState)(int goalstate); void (*BotResetAvoidGoals)(int goalstate); void (*BotRemoveFromAvoidGoals)(int goalstate, int number); void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal); void (*BotPopGoal)(int goalstate); void (*BotEmptyGoalStack)(int goalstate); void (*BotDumpAvoidGoals)(int goalstate); void (*BotDumpGoalStack)(int goalstate); void (*BotGoalName)(int number, char *name, int size); int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal); int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal); int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags); int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags, struct bot_goal_s *ltg, float maxtime); int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal); int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal); int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal); int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal); int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal); float (*BotAvoidGoalTime)(int goalstate, int number); void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime); void (*BotInitLevelItems)(void); void (*BotUpdateEntityItems)(void); int (*BotLoadItemWeights)(int goalstate, char *filename); void (*BotFreeItemWeights)(int goalstate); void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child); void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename); void (*BotMutateGoalFuzzyLogic)(int goalstate, float range); int (*BotAllocGoalState)(int client); void (*BotFreeGoalState)(int handle); //----------------------------------- // be_ai_move.h //----------------------------------- void (*BotResetMoveState)(int movestate); void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags); int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type); void (*BotResetAvoidReach)(int movestate); void (*BotResetLastAvoidReach)(int movestate); int (*BotReachabilityArea)(vec3_t origin, int testground); int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target); int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target); int (*BotAllocMoveState)(void); void (*BotFreeMoveState)(int handle); void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove); void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type); //----------------------------------- // be_ai_weap.h //----------------------------------- int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory); void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo); int (*BotLoadWeaponWeights)(int weaponstate, char *filename); int (*BotAllocWeaponState)(void); void (*BotFreeWeaponState)(int weaponstate); void (*BotResetWeaponState)(int weaponstate); //----------------------------------- // be_ai_gen.h //----------------------------------- int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child); } ai_export_t; //bot AI library imported functions typedef struct botlib_export_s { //Area Awareness System functions aas_export_t aas; //Elementary Action functions ea_export_t ea; //AI functions ai_export_t ai; //setup the bot library, returns BLERR_ int (*BotLibSetup)(void); //shutdown the bot library, returns BLERR_ int (*BotLibShutdown)(void); //sets a library variable returns BLERR_ int (*BotLibVarSet)(char *var_name, char *value); //gets a library variable returns BLERR_ int (*BotLibVarGet)(char *var_name, char *value, int size); //sets a C-like define returns BLERR_ int (*PC_AddGlobalDefine)(char *string); int (*PC_LoadSourceHandle)(const char *filename); int (*PC_FreeSourceHandle)(int handle); int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token); int (*PC_SourceFileAndLine)(int handle, char *filename, int *line); //start a frame in the bot library int (*BotLibStartFrame)(float time); //load a new map in the bot library int (*BotLibLoadMap)(const char *mapname); //entity updates int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state); //just for testing int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3); } botlib_export_t; //linking of bot library botlib_export_t *GetBotLibAPI( int apiVersion, botlib_import_t *import ); /* Library variables: name: default: module(s): description: "basedir" "" l_utils.c base directory "gamedir" "" l_utils.c game directory "cddir" "" l_utils.c CD directory "log" "0" l_log.c enable/disable creating a log file "maxclients" "4" be_interface.c maximum number of clients "maxentities" "1024" be_interface.c maximum number of entities "bot_developer" "0" be_interface.c bot developer mode "phys_friction" "6" be_aas_move.c ground friction "phys_stopspeed" "100" be_aas_move.c stop speed "phys_gravity" "800" be_aas_move.c gravity value "phys_waterfriction" "1" be_aas_move.c water friction "phys_watergravity" "400" be_aas_move.c gravity in water "phys_maxvelocity" "320" be_aas_move.c maximum velocity "phys_maxwalkvelocity" "320" be_aas_move.c maximum walk velocity "phys_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity "phys_maxswimvelocity" "150" be_aas_move.c maximum swim velocity "phys_walkaccelerate" "10" be_aas_move.c walk acceleration "phys_airaccelerate" "1" be_aas_move.c air acceleration "phys_swimaccelerate" "4" be_aas_move.c swim acceleration "phys_maxstep" "18" be_aas_move.c maximum step height "phys_maxsteepness" "0.7" be_aas_move.c maximum floor steepness "phys_maxbarrier" "32" be_aas_move.c maximum barrier height "phys_maxwaterjump" "19" be_aas_move.c maximum waterjump height "phys_jumpvel" "270" be_aas_move.c jump z velocity "phys_falldelta5" "40" be_aas_move.c "phys_falldelta10" "60" be_aas_move.c "rs_waterjump" "400" be_aas_move.c "rs_teleport" "50" be_aas_move.c "rs_barrierjump" "100" be_aas_move.c "rs_startcrouch" "300" be_aas_move.c "rs_startgrapple" "500" be_aas_move.c "rs_startwalkoffledge" "70" be_aas_move.c "rs_startjump" "300" be_aas_move.c "rs_rocketjump" "500" be_aas_move.c "rs_bfgjump" "500" be_aas_move.c "rs_jumppad" "250" be_aas_move.c "rs_aircontrolledjumppad" "300" be_aas_move.c "rs_funcbob" "300" be_aas_move.c "rs_startelevator" "50" be_aas_move.c "rs_falldamage5" "300" be_aas_move.c "rs_falldamage10" "500" be_aas_move.c "rs_maxjumpfallheight" "450" be_aas_move.c "max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS "max_routingcache" "4096" be_aas_route.c maximum routing cache size in KB "forceclustering" "0" be_aas_main.c force recalculation of clusters "forcereachability" "0" be_aas_main.c force recalculation of reachabilities "forcewrite" "0" be_aas_main.c force writing of aas file "aasoptimize" "0" be_aas_main.c enable aas optimization "sv_mapChecksum" "0" be_aas_main.c BSP file checksum "bot_visualizejumppads" "0" be_aas_reach.c visualize jump pads "bot_reloadcharacters" "0" - reload bot character files "ai_gametype" "0" be_ai_goal.c game type "droppedweight" "1000" be_ai_goal.c additional dropped item weight "weapindex_rocketlauncher" "5" be_ai_move.c rl weapon index for rocket jumping "weapindex_bfg10k" "9" be_ai_move.c bfg weapon index for bfg jumping "weapindex_grapple" "10" be_ai_move.c grapple weapon index for grappling "entitytypemissile" "3" be_ai_move.c ET_MISSILE "offhandgrapple" "0" be_ai_move.c enable off hand grapple hook "cmd_grappleon" "grappleon" be_ai_move.c command to activate off hand grapple "cmd_grappleoff" "grappleoff" be_ai_move.c command to deactivate off hand grapple "itemconfig" "items.c" be_ai_goal.c item configuration file "weaponconfig" "weapons.c" be_ai_weap.c weapon configuration file "synfile" "syn.c" be_ai_chat.c file with synonyms "rndfile" "rnd.c" be_ai_chat.c file with random strings "matchfile" "match.c" be_ai_chat.c file with match strings "nochat" "0" be_ai_chat.c disable chats "max_messages" "1024" be_ai_chat.c console message heap size "max_weaponinfo" "32" be_ai_weap.c maximum number of weapon info "max_projectileinfo" "32" be_ai_weap.c maximum number of projectile info "max_iteminfo" "256" be_ai_goal.c maximum number of item info "max_levelitems" "256" be_ai_goal.c maximum number of level items */ openarena_0.8.8.orig/code/botlib/be_aas.h0000644000175000017500000001757511656310264017015 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_aas.h * * desc: Area Awareness System, stuff exported to the AI * * $Archive: /source/code/botlib/be_aas.h $ * *****************************************************************************/ #ifndef MAX_STRINGFIELD #define MAX_STRINGFIELD 80 #endif //travel flags #define TFL_INVALID 0x00000001 //traveling temporary not possible #define TFL_WALK 0x00000002 //walking #define TFL_CROUCH 0x00000004 //crouching #define TFL_BARRIERJUMP 0x00000008 //jumping onto a barrier #define TFL_JUMP 0x00000010 //jumping #define TFL_LADDER 0x00000020 //climbing a ladder #define TFL_WALKOFFLEDGE 0x00000080 //walking of a ledge #define TFL_SWIM 0x00000100 //swimming #define TFL_WATERJUMP 0x00000200 //jumping out of the water #define TFL_TELEPORT 0x00000400 //teleporting #define TFL_ELEVATOR 0x00000800 //elevator #define TFL_ROCKETJUMP 0x00001000 //rocket jumping #define TFL_BFGJUMP 0x00002000 //bfg jumping #define TFL_GRAPPLEHOOK 0x00004000 //grappling hook #define TFL_DOUBLEJUMP 0x00008000 //double jump #define TFL_RAMPJUMP 0x00010000 //ramp jump #define TFL_STRAFEJUMP 0x00020000 //strafe jump #define TFL_JUMPPAD 0x00040000 //jump pad #define TFL_AIR 0x00080000 //travel through air #define TFL_WATER 0x00100000 //travel through water #define TFL_SLIME 0x00200000 //travel through slime #define TFL_LAVA 0x00400000 //travel through lava #define TFL_DONOTENTER 0x00800000 //travel through donotenter area #define TFL_FUNCBOB 0x01000000 //func bobbing #define TFL_FLIGHT 0x02000000 //flight #define TFL_BRIDGE 0x04000000 //move over a bridge // #define TFL_NOTTEAM1 0x08000000 //not team 1 #define TFL_NOTTEAM2 0x10000000 //not team 2 //default travel flags #define TFL_DEFAULT TFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\ TFL_JUMP|TFL_LADDER|\ TFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\ TFL_TELEPORT|TFL_ELEVATOR|\ TFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB typedef enum { SOLID_NOT, // no interaction with other objects SOLID_TRIGGER, // only touch when inside, after moving SOLID_BBOX, // touch on edge SOLID_BSP // bsp clip, touch on edge } solid_t; //a trace is returned when a box is swept through the AAS world typedef struct aas_trace_s { qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position int ent; // entity blocking the trace int lastarea; // last area the trace was in (zero if none) int area; // area blocking the trace (zero if none) int planenum; // number of the plane that was hit } aas_trace_t; /* Defined in botlib.h //bsp_trace_t hit surface typedef struct bsp_surface_s { char name[16]; int flags; int value; } bsp_surface_t; //a trace is returned when a box is swept through the BSP world typedef struct bsp_trace_s { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position cplane_t plane; // surface normal at impact float exp_dist; // expanded plane distance int sidenum; // number of the brush side hit bsp_surface_t surface; // hit surface int contents; // contents on other side of surface hit int ent; // number of entity hit } bsp_trace_t; // */ //entity info typedef struct aas_entityinfo_s { int valid; // true if updated this frame int type; // entity type int flags; // entity flags float ltime; // local time float update_time; // time between last and current update int number; // number of the entity vec3_t origin; // origin of the entity vec3_t angles; // angles of the model vec3_t old_origin; // for lerping vec3_t lastvisorigin; // last visible origin vec3_t mins; // bounding box minimums vec3_t maxs; // bounding box maximums int groundent; // ground entity int solid; // solid type int modelindex; // model used int modelindex2; // weapons, CTF flags, etc int frame; // model frame number int event; // impulse events -- muzzle flashes, footsteps, etc int eventParm; // even parameter int powerups; // bit flags int weapon; // determines weapon and flash model, etc int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT } aas_entityinfo_t; // area info typedef struct aas_areainfo_s { int contents; int flags; int presencetype; int cluster; vec3_t mins; vec3_t maxs; vec3_t center; } aas_areainfo_t; // client movement prediction stop events, stop as soon as: #define SE_NONE 0 #define SE_HITGROUND 1 // the ground is hit #define SE_LEAVEGROUND 2 // there's no ground #define SE_ENTERWATER 4 // water is entered #define SE_ENTERSLIME 8 // slime is entered #define SE_ENTERLAVA 16 // lava is entered #define SE_HITGROUNDDAMAGE 32 // the ground is hit with damage #define SE_GAP 64 // there's a gap #define SE_TOUCHJUMPPAD 128 // touching a jump pad area #define SE_TOUCHTELEPORTER 256 // touching teleporter #define SE_ENTERAREA 512 // the given stoparea is entered #define SE_HITGROUNDAREA 1024 // a ground face in the area is hit #define SE_HITBOUNDINGBOX 2048 // hit the specified bounding box #define SE_TOUCHCLUSTERPORTAL 4096 // touching a cluster portal typedef struct aas_clientmove_s { vec3_t endpos; //position at the end of movement prediction int endarea; //area at end of movement prediction vec3_t velocity; //velocity at the end of movement prediction aas_trace_t trace; //last trace int presencetype; //presence type at end of movement prediction int stopevent; //event that made the prediction stop int endcontents; //contents at the end of movement prediction float time; //time predicted ahead int frames; //number of frames predicted ahead } aas_clientmove_t; // alternate route goals #define ALTROUTEGOAL_ALL 1 #define ALTROUTEGOAL_CLUSTERPORTALS 2 #define ALTROUTEGOAL_VIEWPORTALS 4 typedef struct aas_altroutegoal_s { vec3_t origin; int areanum; unsigned short starttraveltime; unsigned short goaltraveltime; unsigned short extratraveltime; } aas_altroutegoal_t; // route prediction stop events #define RSE_NONE 0 #define RSE_NOROUTE 1 //no route to goal #define RSE_USETRAVELTYPE 2 //stop as soon as on of the given travel types is used #define RSE_ENTERCONTENTS 4 //stop when entering the given contents #define RSE_ENTERAREA 8 //stop when entering the given area typedef struct aas_predictroute_s { vec3_t endpos; //position at the end of movement prediction int endarea; //area at end of movement prediction int stopevent; //event that made the prediction stop int endcontents; //contents at the end of movement prediction int endtravelflags; //end travel flags int numareas; //number of areas predicted ahead int time; //time predicted ahead (in hundreth of a sec) } aas_predictroute_t; openarena_0.8.8.orig/code/botlib/be_ai_chat.h0000644000175000017500000001062611656310264017627 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_chat.h * * desc: char AI * * $Archive: /source/code/botlib/be_ai_chat.h $ * *****************************************************************************/ #define MAX_MESSAGE_SIZE 256 #define MAX_CHATTYPE_NAME 32 #define MAX_MATCHVARIABLES 8 #define CHAT_GENDERLESS 0 #define CHAT_GENDERFEMALE 1 #define CHAT_GENDERMALE 2 #define CHAT_ALL 0 #define CHAT_TEAM 1 #define CHAT_TELL 2 //a console message typedef struct bot_consolemessage_s { int handle; float time; //message time int type; //message type char message[MAX_MESSAGE_SIZE]; //message struct bot_consolemessage_s *prev, *next; //prev and next in list } bot_consolemessage_t; //match variable typedef struct bot_matchvariable_s { char offset; int length; } bot_matchvariable_t; //returned to AI when a match is found typedef struct bot_match_s { char string[MAX_MESSAGE_SIZE]; int type; int subtype; bot_matchvariable_t variables[MAX_MATCHVARIABLES]; } bot_match_t; //setup the chat AI int BotSetupChatAI(void); //shutdown the chat AI void BotShutdownChatAI(void); //returns the handle to a newly allocated chat state int BotAllocChatState(void); //frees the chatstate void BotFreeChatState(int handle); //adds a console message to the chat state void BotQueueConsoleMessage(int chatstate, int type, char *message); //removes the console message from the chat state void BotRemoveConsoleMessage(int chatstate, int handle); //returns the next console message from the state int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm); //returns the number of console messages currently stored in the state int BotNumConsoleMessages(int chatstate); //selects a chat message of the given type void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); //returns the number of initial chat messages of the given type int BotNumInitialChats(int chatstate, char *type); //find and select a reply for the given message int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); //returns the length of the currently selected chat message int BotChatLength(int chatstate); //enters the selected chat message void BotEnterChat(int chatstate, int clientto, int sendto); //get the chat message ready to be output void BotGetChatMessage(int chatstate, char *buf, int size); //checks if the first string contains the second one, returns index into first string or -1 if not found int StringContains(char *str1, char *str2, int casesensitive); //finds a match for the given string using the match templates int BotFindMatch(char *str, bot_match_t *match, unsigned long int context); //returns a variable from a match void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size); //unify all the white spaces in the string void UnifyWhiteSpaces(char *string); //replace all the context related synonyms in the string void BotReplaceSynonyms(char *string, unsigned long int context); //loads a chat file for the chat state int BotLoadChatFile(int chatstate, char *chatfile, char *chatname); //store the gender of the bot in the chat state void BotSetChatGender(int chatstate, int gender); //store the bot name in the chat state void BotSetChatName(int chatstate, char *name, int client); openarena_0.8.8.orig/code/botlib/be_aas_reach.h0000644000175000017500000000544111656310264020144 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_reach.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_reach.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize calculating the reachabilities void AAS_InitReachability(void); //continue calculating the reachabilities int AAS_ContinueInitReachability(float time); // int AAS_BestReachableLinkArea(aas_link_t *areas); #endif //AASINTERN //returns true if the are has reachabilities to other areas int AAS_AreaReachability(int areanum); //returns the best reachable area and goal origin for a bounding box at the given origin int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin); //returns the best jumppad area from which the bbox at origin is reachable int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs); //returns the next reachability using the given model int AAS_NextModelReachability(int num, int modelnum); //returns the total area of the ground faces of the given area float AAS_AreaGroundFaceArea(int areanum); //returns true if the area is crouch only int AAS_AreaCrouch(int areanum); //returns true if a player can swim in this area int AAS_AreaSwim(int areanum); //returns true if the area is filled with a liquid int AAS_AreaLiquid(int areanum); //returns true if the area contains lava int AAS_AreaLava(int areanum); //returns true if the area contains slime int AAS_AreaSlime(int areanum); //returns true if the area has one or more ground faces int AAS_AreaGrounded(int areanum); //returns true if the area has one or more ladder faces int AAS_AreaLadder(int areanum); //returns true if the area is a jump pad int AAS_AreaJumpPad(int areanum); //returns true if the area is donotenter int AAS_AreaDoNotEnter(int areanum); openarena_0.8.8.orig/code/botlib/be_aas_def.h0000644000175000017500000002042711656310264017621 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_def.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_def.h $ * *****************************************************************************/ //debugging on #define AAS_DEBUG #define MAX_CLIENTS 64 #define MAX_MODELS 256 // these are sent over the net as 8 bits #define MAX_SOUNDS 256 // so they cannot be blindly increased #define MAX_CONFIGSTRINGS 1024 #define CS_SCORES 32 #define CS_MODELS (CS_SCORES+MAX_CLIENTS) #define CS_SOUNDS (CS_MODELS+MAX_MODELS) #define DF_AASENTNUMBER(x) (x - aasworld.entities) #define DF_NUMBERAASENT(x) (&aasworld.entities[x]) #define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) #define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) #ifndef MAX_PATH #define MAX_PATH MAX_QPATH #endif //string index (for model, sound and image index) typedef struct aas_stringindex_s { int numindexes; char **index; } aas_stringindex_t; //structure to link entities to areas and areas to entities typedef struct aas_link_s { int entnum; int areanum; struct aas_link_s *next_ent, *prev_ent; struct aas_link_s *next_area, *prev_area; } aas_link_t; //structure to link entities to leaves and leaves to entities typedef struct bsp_link_s { int entnum; int leafnum; struct bsp_link_s *next_ent, *prev_ent; struct bsp_link_s *next_leaf, *prev_leaf; } bsp_link_t; typedef struct bsp_entdata_s { vec3_t origin; vec3_t angles; vec3_t absmins; vec3_t absmaxs; int solid; int modelnum; } bsp_entdata_t; //entity typedef struct aas_entity_s { //entity info aas_entityinfo_t i; //links into the AAS areas aas_link_t *areas; //links into the BSP leaves bsp_link_t *leaves; } aas_entity_t; typedef struct aas_settings_s { vec3_t phys_gravitydirection; float phys_friction; float phys_stopspeed; float phys_gravity; float phys_waterfriction; float phys_watergravity; float phys_maxvelocity; float phys_maxwalkvelocity; float phys_maxcrouchvelocity; float phys_maxswimvelocity; float phys_walkaccelerate; float phys_airaccelerate; float phys_swimaccelerate; float phys_maxstep; float phys_maxsteepness; float phys_maxwaterjump; float phys_maxbarrier; float phys_jumpvel; float phys_falldelta5; float phys_falldelta10; float rs_waterjump; float rs_teleport; float rs_barrierjump; float rs_startcrouch; float rs_startgrapple; float rs_startwalkoffledge; float rs_startjump; float rs_rocketjump; float rs_bfgjump; float rs_jumppad; float rs_aircontrolledjumppad; float rs_funcbob; float rs_startelevator; float rs_falldamage5; float rs_falldamage10; float rs_maxfallheight; float rs_maxjumpfallheight; } aas_settings_t; #define CACHETYPE_PORTAL 0 #define CACHETYPE_AREA 1 //routing cache typedef struct aas_routingcache_s { byte type; //portal or area cache float time; //last time accessed or updated int size; //size of the routing cache int cluster; //cluster the cache is for int areanum; //area the cache is created for vec3_t origin; //origin within the area float starttraveltime; //travel time to start with int travelflags; //combinations of the travel flags struct aas_routingcache_s *prev, *next; struct aas_routingcache_s *time_prev, *time_next; unsigned char *reachabilities; //reachabilities used for routing unsigned short int traveltimes[1]; //travel time for every area (variable sized) } aas_routingcache_t; //fields for the routing algorithm typedef struct aas_routingupdate_s { int cluster; int areanum; //area number of the update vec3_t start; //start point the area was entered unsigned short int tmptraveltime; //temporary travel time unsigned short int *areatraveltimes; //travel times within the area qboolean inlist; //true if the update is in the list struct aas_routingupdate_s *next; struct aas_routingupdate_s *prev; } aas_routingupdate_t; //reversed reachability link typedef struct aas_reversedlink_s { int linknum; //the aas_areareachability_t int areanum; //reachable from this area struct aas_reversedlink_s *next; //next link } aas_reversedlink_t; //reversed area reachability typedef struct aas_reversedreachability_s { int numlinks; aas_reversedlink_t *first; } aas_reversedreachability_t; //areas a reachability goes through typedef struct aas_reachabilityareas_s { int firstarea, numareas; } aas_reachabilityareas_t; typedef struct aas_s { int loaded; //true when an AAS file is loaded int initialized; //true when AAS has been initialized int savefile; //set true when file should be saved int bspchecksum; //current time float time; int numframes; //name of the aas file char filename[MAX_PATH]; char mapname[MAX_PATH]; //bounding boxes int numbboxes; aas_bbox_t *bboxes; //vertexes int numvertexes; aas_vertex_t *vertexes; //planes int numplanes; aas_plane_t *planes; //edges int numedges; aas_edge_t *edges; //edge index int edgeindexsize; aas_edgeindex_t *edgeindex; //faces int numfaces; aas_face_t *faces; //face index int faceindexsize; aas_faceindex_t *faceindex; //convex areas int numareas; aas_area_t *areas; //convex area settings int numareasettings; aas_areasettings_t *areasettings; //reachablity list int reachabilitysize; aas_reachability_t *reachability; //nodes of the bsp tree int numnodes; aas_node_t *nodes; //cluster portals int numportals; aas_portal_t *portals; //cluster portal index int portalindexsize; aas_portalindex_t *portalindex; //clusters int numclusters; aas_cluster_t *clusters; // int numreachabilityareas; float reachabilitytime; //enities linked in the areas aas_link_t *linkheap; //heap with link structures int linkheapsize; //size of the link heap aas_link_t *freelinks; //first free link aas_link_t **arealinkedentities; //entities linked into areas //entities int maxentities; int maxclients; aas_entity_t *entities; //string indexes char *configstrings[MAX_CONFIGSTRINGS]; int indexessetup; //index to retrieve travel flag for a travel type int travelflagfortype[MAX_TRAVELTYPES]; //travel flags for each area based on contents int *areacontentstravelflags; //routing update aas_routingupdate_t *areaupdate; aas_routingupdate_t *portalupdate; //number of routing updates during a frame (reset every frame) int frameroutingupdates; //reversed reachability links aas_reversedreachability_t *reversedreachability; //travel times within the areas unsigned short ***areatraveltimes; //array of size numclusters with cluster cache aas_routingcache_t ***clusterareacache; aas_routingcache_t **portalcache; //cache list sorted on time aas_routingcache_t *oldestcache; // start of cache list sorted on time aas_routingcache_t *newestcache; // end of cache list sorted on time //maximum travel time through portal areas int *portalmaxtraveltimes; //areas the reachabilities go through int *reachabilityareaindex; aas_reachabilityareas_t *reachabilityareas; } aas_t; #define AASINTERN #ifndef BSPCINCLUDE #include "be_aas_main.h" #include "be_aas_entity.h" #include "be_aas_sample.h" #include "be_aas_cluster.h" #include "be_aas_reach.h" #include "be_aas_route.h" #include "be_aas_routealt.h" #include "be_aas_debug.h" #include "be_aas_file.h" #include "be_aas_optimize.h" #include "be_aas_bsp.h" #include "be_aas_move.h" #endif //BSPCINCLUDE openarena_0.8.8.orig/code/botlib/be_aas_entity.h0000644000175000017500000000472611656310264020403 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_entity.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_entity.h $ * *****************************************************************************/ #ifdef AASINTERN //invalidates all entity infos void AAS_InvalidateEntities(void); //unlink not updated entities void AAS_UnlinkInvalidEntities(void); //resets the entity AAS and BSP links (sets areas and leaves pointers to NULL) void AAS_ResetEntityLinks(void); //updates an entity int AAS_UpdateEntity(int ent, bot_entitystate_t *state); //gives the entity data used for collision detection void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata); #endif //AASINTERN //returns the size of the entity bounding box in mins and maxs void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs); //returns the BSP model number of the entity int AAS_EntityModelNum(int entnum); //returns the origin of an entity with the given model number int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin); //returns the best reachable area the entity is situated in int AAS_BestReachableEntityArea(int entnum); //returns the info of the given entity void AAS_EntityInfo(int entnum, aas_entityinfo_t *info); //returns the next entity int AAS_NextEntity(int entnum); //returns the origin of the entity void AAS_EntityOrigin(int entnum, vec3_t origin); //returns the entity type int AAS_EntityType(int entnum); //returns the model index of the entity int AAS_EntityModelindex(int entnum); openarena_0.8.8.orig/code/botlib/be_aas_debug.h0000644000175000017500000000454211656310264020151 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_debug.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_debug.h $ * *****************************************************************************/ //clear the shown debug lines void AAS_ClearShownDebugLines(void); // void AAS_ClearShownPolygons(void); //show a debug line void AAS_DebugLine(vec3_t start, vec3_t end, int color); //show a permenent line void AAS_PermanentLine(vec3_t start, vec3_t end, int color); //show a permanent cross void AAS_DrawPermanentCross(vec3_t origin, float size, int color); //draw a cross in the plane void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color); //show a bounding box void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs); //show a face void AAS_ShowFace(int facenum); //show an area void AAS_ShowArea(int areanum, int groundfacesonly); // void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly); //draw a cros void AAS_DrawCross(vec3_t origin, float size, int color); //print the travel type void AAS_PrintTravelType(int traveltype); //draw an arrow void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor); //visualize the given reachability void AAS_ShowReachability(struct aas_reachability_s *reach); //show the reachable areas from the given area void AAS_ShowReachableAreas(int areanum); openarena_0.8.8.orig/code/botlib/be_aas_cluster.h0000644000175000017500000000255311656310264020544 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_cluster.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_cluster.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize the AAS clustering void AAS_InitClustering(void); // void AAS_SetViewPortalsAsClusterPortals(void); #endif //AASINTERN openarena_0.8.8.orig/code/botlib/be_interface.h0000644000175000017500000000356111656310264020177 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_interface.h * * desc: botlib interface * * $Archive: /source/code/botlib/be_interface.h $ * *****************************************************************************/ //#define DEBUG //debug code #define RANDOMIZE //randomize bot behaviour //FIXME: get rid of this global structure typedef struct botlib_globals_s { int botlibsetup; //true when the bot library has been setup int maxentities; //maximum number of entities int maxclients; //maximum number of clients float time; //the global time #ifdef DEBUG qboolean debug; //true if debug is on int goalareanum; vec3_t goalorigin; int runai; #endif } botlib_globals_t; extern botlib_globals_t botlibglobals; extern botlib_import_t botimport; extern int bot_developer; //true if developer is on // int Sys_MilliSeconds(void); openarena_0.8.8.orig/code/botlib/lcc.mak0000644000175000017500000000166311656310264016654 0ustar smcvsmcv# # Makefile for Gladiator Bot library: gladiator.dll # Intended for LCC-Win32 # CC=lcc CFLAGS=-DC_ONLY -o OBJS= be_aas_bspq2.obj \ be_aas_bsphl.obj \ be_aas_cluster.obj \ be_aas_debug.obj \ be_aas_entity.obj \ be_aas_file.obj \ be_aas_light.obj \ be_aas_main.obj \ be_aas_move.obj \ be_aas_optimize.obj \ be_aas_reach.obj \ be_aas_route.obj \ be_aas_routealt.obj \ be_aas_sample.obj \ be_aas_sound.obj \ be_ai2_dm.obj \ be_ai2_dmnet.obj \ be_ai2_main.obj \ be_ai_char.obj \ be_ai_chat.obj \ be_ai_goal.obj \ be_ai_load.obj \ be_ai_move.obj \ be_ai_weap.obj \ be_ai_weight.obj \ be_ea.obj \ be_interface.obj \ l_crc.obj \ l_libvar.obj \ l_log.obj \ l_memory.obj \ l_precomp.obj \ l_script.obj \ l_struct.obj \ l_utils.obj \ q_shared.obj all: gladiator.dll gladiator.dll: $(OBJS) lcclnk -dll -entry GetBotAPI *.obj botlib.def -o gladiator.dll clean: del *.obj gladiator.dll %.obj: %.c $(CC) $(CFLAGS) $< openarena_0.8.8.orig/code/botlib/be_aas_route.h0000644000175000017500000000551511656310264020222 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_route.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_route.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize the AAS routing void AAS_InitRouting(void); //free the AAS routing caches void AAS_FreeRoutingCaches(void); //returns the travel time from start to end in the given area unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); // void AAS_CreateAllRoutingCache(void); void AAS_WriteRouteCache(void); // void AAS_RoutingInfo(void); #endif //AASINTERN //returns the travel flag for the given travel type int AAS_TravelFlagForType(int traveltype); //return the travel flag(s) for traveling through this area int AAS_AreaContentsTravelFlags(int areanum); //returns the index of the next reachability for the given area int AAS_NextAreaReachability(int areanum, int reachnum); //returns the reachability with the given index void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach); //returns a random goal area and goal origin int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin); //enable or disable an area for routing int AAS_EnableRoutingArea(int areanum, int enable); //returns the travel time within the given area from start to end unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); //returns the travel time from the area to the goal area using the given travel flags int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); //predict a route up to a stop event int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum); openarena_0.8.8.orig/code/botlib/l_crc.h0000644000175000017500000000242511656310264016651 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ typedef unsigned short crc_t; void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_ProcessString(unsigned char *data, int length); void CRC_ContinueProcessString(unsigned short *crc, char *data, int length); openarena_0.8.8.orig/code/botlib/l_log.h0000644000175000017500000000320111656310264016654 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_log.h * * desc: log file * * $Archive: /source/code/botlib/l_log.h $ * *****************************************************************************/ //open a log file void Log_Open(char *filename); //close the current log file void Log_Close(void); //close log file if present void Log_Shutdown(void); //write to the current opened log file void QDECL Log_Write(char *fmt, ...); //write to the current opened log file with a time stamp void QDECL Log_WriteTimeStamped(char *fmt, ...); //returns a pointer to the log file FILE *Log_FilePointer(void); //flush log file void Log_Flush(void); openarena_0.8.8.orig/code/botlib/be_aas_file.h0000644000175000017500000000300411656310264017772 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_file.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_file.h $ * *****************************************************************************/ #ifdef AASINTERN //loads the AAS file with the given name int AAS_LoadAASFile(char *filename); //writes an AAS file with the given name qboolean AAS_WriteAASFile(char *filename); //dumps the loaded AAS data void AAS_DumpAASData(void); //print AAS file information void AAS_FileInfo(void); #endif //AASINTERN openarena_0.8.8.orig/code/botlib/be_aas_move.h0000644000175000017500000000553311656310264020032 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_move.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_move.h $ * *****************************************************************************/ #ifdef AASINTERN extern aas_settings_t aassettings; #endif //AASINTERN //movement prediction int AAS_PredictClientMovement(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize); //predict movement until bounding box is hit int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, vec3_t mins, vec3_t maxs, int visualize); //returns true if on the ground at the given origin int AAS_OnGround(vec3_t origin, int presencetype, int passent); //returns true if swimming at the given origin int AAS_Swimming(vec3_t origin); //returns the jump reachability run start point void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart); //returns true if against a ladder at the given origin int AAS_AgainstLadder(vec3_t origin); //rocket jump Z velocity when rocket-jumping at origin float AAS_RocketJumpZVelocity(vec3_t origin); //bfg jump Z velocity when bfg-jumping at origin float AAS_BFGJumpZVelocity(vec3_t origin); //calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity); // void AAS_SetMovedir(vec3_t angles, vec3_t movedir); // int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs); // void AAS_InitSettings(void); openarena_0.8.8.orig/code/botlib/be_aas_main.h0000644000175000017500000000403411656310264020003 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_main.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_main.h $ * *****************************************************************************/ #ifdef AASINTERN extern aas_t aasworld; //AAS error message void QDECL AAS_Error(char *fmt, ...); //set AAS initialized void AAS_SetInitialized(void); //setup AAS with the given number of entities and clients int AAS_Setup(void); //shutdown AAS void AAS_Shutdown(void); //start a new map int AAS_LoadMap(const char *mapname); //start a new time frame int AAS_StartFrame(float time); #endif //AASINTERN //returns true if AAS is initialized int AAS_Initialized(void); //returns true if the AAS file is loaded int AAS_Loaded(void); //returns the model name from the given index char *AAS_ModelFromIndex(int index); //returns the index from the given model name int AAS_IndexFromModel(char *modelname); //returns the current time float AAS_Time(void); // void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ); openarena_0.8.8.orig/code/botlib/be_aas_sample.h0000644000175000017500000000607511656310264020347 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_sample.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_sample.h $ * *****************************************************************************/ #ifdef AASINTERN void AAS_InitAASLinkHeap(void); void AAS_InitAASLinkedEntities(void); void AAS_FreeAASLinkHeap(void); void AAS_FreeAASLinkedEntities(void); aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point); aas_face_t *AAS_TraceEndFace(aas_trace_t *trace); aas_plane_t *AAS_PlaneFromNum(int planenum); aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum); aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype); qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon); qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon); void AAS_UnlinkFromAreas(aas_link_t *areas); #endif //AASINTERN //returns the mins and maxs of the bounding box for the given presence type void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); //returns the cluster the area is in (negative portal number if the area is a portal) int AAS_AreaCluster(int areanum); //returns the presence type(s) of the area int AAS_AreaPresenceType(int areanum); //returns the presence type(s) at the given point int AAS_PointPresenceType(vec3_t point); //returns the result of the trace of a client bbox aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent); //stores the areas the trace went through and returns the number of passed areas int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); //returns the areas the bounding box is in int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); //return area information int AAS_AreaInfo( int areanum, aas_areainfo_t *info ); //returns the area the point is in int AAS_PointAreaNum(vec3_t point); // int AAS_PointReachabilityAreaIndex( vec3_t point ); //returns the plane the given face is in void AAS_FacePlane(int facenum, vec3_t normal, float *dist); openarena_0.8.8.orig/code/botlib/l_utils.h0000644000175000017500000000257011656310264017243 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_util.h * * desc: utils * * $Archive: /source/code/botlib/l_util.h $ * *****************************************************************************/ #define Vector2Angles(v,a) vectoangles(v,a) #ifndef MAX_PATH #define MAX_PATH MAX_QPATH #endif #define Maximum(x,y) (x > y ? x : y) #define Minimum(x,y) (x < y ? x : y) openarena_0.8.8.orig/code/botlib/be_ai_gen.h0000644000175000017500000000250511656310264017456 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_gen.h * * desc: genetic selection * * $Archive: /source/code/botlib/be_ai_gen.h $ * *****************************************************************************/ int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); openarena_0.8.8.orig/code/botlib/l_memory.h0000644000175000017500000000573011656310264017414 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_memory.h * * desc: memory management * * $Archive: /source/code/botlib/l_memory.h $ * *****************************************************************************/ //#define MEMDEBUG #ifdef MEMDEBUG #define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); #define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); //allocate a memory block of the given size void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); //allocate a memory block of the given size and clear it void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); // #define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__); #define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__); //allocate a memory block of the given size void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line); //allocate a memory block of the given size and clear it void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line); #else //allocate a memory block of the given size void *GetMemory(unsigned long size); //allocate a memory block of the given size and clear it void *GetClearedMemory(unsigned long size); // #ifdef BSPC #define GetHunkMemory GetMemory #define GetClearedHunkMemory GetClearedMemory #else //allocate a memory block of the given size void *GetHunkMemory(unsigned long size); //allocate a memory block of the given size and clear it void *GetClearedHunkMemory(unsigned long size); #endif #endif //free the given memory block void FreeMemory(void *ptr); //returns the amount available memory int AvailableMemory(void); //prints the total used memory size void PrintUsedMemorySize(void); //print all memory blocks with label void PrintMemoryLabels(void); //returns the size of the memory block in bytes int MemoryByteSize(void *ptr); //free all allocated memory void DumpMemory(void); openarena_0.8.8.orig/code/botlib/be_aas_routealt.h0000644000175000017500000000302411656310264020714 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_routealt.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_routealt.h $ * *****************************************************************************/ #ifdef AASINTERN void AAS_InitAlternativeRouting(void); void AAS_ShutdownAlternativeRouting(void); #endif //AASINTERN int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, int type); openarena_0.8.8.orig/code/botlib/aasfile.h0000644000175000017500000002134411656310264017174 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //NOTE: int = default signed // default long #define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') #define AASVERSION_OLD 4 #define AASVERSION 5 //presence types #define PRESENCE_NONE 1 #define PRESENCE_NORMAL 2 #define PRESENCE_CROUCH 4 //travel types #define MAX_TRAVELTYPES 32 #define TRAVEL_INVALID 1 //temporary not possible #define TRAVEL_WALK 2 //walking #define TRAVEL_CROUCH 3 //crouching #define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier #define TRAVEL_JUMP 5 //jumping #define TRAVEL_LADDER 6 //climbing a ladder #define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge #define TRAVEL_SWIM 8 //swimming #define TRAVEL_WATERJUMP 9 //jump out of the water #define TRAVEL_TELEPORT 10 //teleportation #define TRAVEL_ELEVATOR 11 //travel by elevator #define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel #define TRAVEL_BFGJUMP 13 //bfg jumping required for travel #define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel #define TRAVEL_DOUBLEJUMP 15 //double jump #define TRAVEL_RAMPJUMP 16 //ramp jump #define TRAVEL_STRAFEJUMP 17 //strafe jump #define TRAVEL_JUMPPAD 18 //jump pad #define TRAVEL_FUNCBOB 19 //func bob //additional travel flags #define TRAVELTYPE_MASK 0xFFFFFF #define TRAVELFLAG_NOTTEAM1 (1 << 24) #define TRAVELFLAG_NOTTEAM2 (2 << 24) //face flags #define FACE_SOLID 1 //just solid at the other side #define FACE_LADDER 2 //ladder #define FACE_GROUND 4 //standing on ground when in this face #define FACE_GAP 8 //gap in the ground #define FACE_LIQUID 16 //face seperating two areas with liquid #define FACE_LIQUIDSURFACE 32 //face seperating liquid and air #define FACE_BRIDGE 64 //can walk over this face if bridge is closed //area contents #define AREACONTENTS_WATER 1 #define AREACONTENTS_LAVA 2 #define AREACONTENTS_SLIME 4 #define AREACONTENTS_CLUSTERPORTAL 8 #define AREACONTENTS_TELEPORTAL 16 #define AREACONTENTS_ROUTEPORTAL 32 #define AREACONTENTS_TELEPORTER 64 #define AREACONTENTS_JUMPPAD 128 #define AREACONTENTS_DONOTENTER 256 #define AREACONTENTS_VIEWPORTAL 512 #define AREACONTENTS_MOVER 1024 #define AREACONTENTS_NOTTEAM1 2048 #define AREACONTENTS_NOTTEAM2 4096 //number of model of the mover inside this area #define AREACONTENTS_MODELNUMSHIFT 24 #define AREACONTENTS_MAXMODELNUM 0xFF #define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) //area flags #define AREA_GROUNDED 1 //bot can stand on the ground #define AREA_LADDER 2 //area contains one or more ladder faces #define AREA_LIQUID 4 //area contains a liquid #define AREA_DISABLED 8 //area is disabled for routing when set #define AREA_BRIDGE 16 //area ontop of a bridge //aas file header lumps #define AAS_LUMPS 14 #define AASLUMP_BBOXES 0 #define AASLUMP_VERTEXES 1 #define AASLUMP_PLANES 2 #define AASLUMP_EDGES 3 #define AASLUMP_EDGEINDEX 4 #define AASLUMP_FACES 5 #define AASLUMP_FACEINDEX 6 #define AASLUMP_AREAS 7 #define AASLUMP_AREASETTINGS 8 #define AASLUMP_REACHABILITY 9 #define AASLUMP_NODES 10 #define AASLUMP_PORTALS 11 #define AASLUMP_PORTALINDEX 12 #define AASLUMP_CLUSTERS 13 //========== bounding box ========= //bounding box typedef struct aas_bbox_s { int presencetype; int flags; vec3_t mins, maxs; } aas_bbox_t; //============ settings =========== //reachability to another area typedef struct aas_reachability_s { int areanum; //number of the reachable area int facenum; //number of the face towards the other area int edgenum; //number of the edge towards the other area vec3_t start; //start point of inter area movement vec3_t end; //end point of inter area movement int traveltype; //type of travel required to get to the area unsigned short int traveltime;//travel time of the inter area movement } aas_reachability_t; //area settings typedef struct aas_areasettings_s { //could also add all kind of statistic fields int contents; //contents of the area int areaflags; //several area flags int presencetype; //how a bot can be present in this area int cluster; //cluster the area belongs to, if negative it's a portal int clusterareanum; //number of the area in the cluster int numreachableareas; //number of reachable areas from this one int firstreachablearea; //first reachable area in the reachable area index } aas_areasettings_t; //cluster portal typedef struct aas_portal_s { int areanum; //area that is the actual portal int frontcluster; //cluster at front of portal int backcluster; //cluster at back of portal int clusterareanum[2]; //number of the area in the front and back cluster } aas_portal_t; //cluster portal index typedef int aas_portalindex_t; //cluster typedef struct aas_cluster_s { int numareas; //number of areas in the cluster int numreachabilityareas; //number of areas with reachabilities int numportals; //number of cluster portals int firstportal; //first cluster portal in the index } aas_cluster_t; //============ 3d definition ============ typedef vec3_t aas_vertex_t; //just a plane in the third dimension typedef struct aas_plane_s { vec3_t normal; //normal vector of the plane float dist; //distance of the plane (normal vector * distance = point in plane) int type; } aas_plane_t; //edge typedef struct aas_edge_s { int v[2]; //numbers of the vertexes of this edge } aas_edge_t; //edge index, negative if vertexes are reversed typedef int aas_edgeindex_t; //a face bounds an area, often it will also seperate two areas typedef struct aas_face_s { int planenum; //number of the plane this face is in int faceflags; //face flags (no use to create face settings for just this field) int numedges; //number of edges in the boundary of the face int firstedge; //first edge in the edge index int frontarea; //area at the front of this face int backarea; //area at the back of this face } aas_face_t; //face index, stores a negative index if backside of face typedef int aas_faceindex_t; //area with a boundary of faces typedef struct aas_area_s { int areanum; //number of this area //3d definition int numfaces; //number of faces used for the boundary of the area int firstface; //first face in the face index used for the boundary of the area vec3_t mins; //mins of the area vec3_t maxs; //maxs of the area vec3_t center; //'center' of the area } aas_area_t; //nodes of the bsp tree typedef struct aas_node_s { int planenum; int children[2]; //child nodes of this node, or areas as leaves when negative //when a child is zero it's a solid leaf } aas_node_t; //=========== aas file =============== //header lump typedef struct { int fileofs; int filelen; } aas_lump_t; //aas file header typedef struct aas_header_s { int ident; int version; int bspchecksum; //data entries aas_lump_t lumps[AAS_LUMPS]; } aas_header_t; //====== additional information ====== /* - when a node child is a solid leaf the node child number is zero - two adjacent areas (sharing a plane at opposite sides) share a face this face is a portal between the areas - when an area uses a face from the faceindex with a positive index then the face plane normal points into the area - the face edges are stored counter clockwise using the edgeindex - two adjacent convex areas (sharing a face) only share One face this is a simple result of the areas being convex - the areas can't have a mixture of ground and gap faces other mixtures of faces in one area are allowed - areas with the AREACONTENTS_CLUSTERPORTAL in the settings have the cluster number set to the negative portal number - edge zero is a dummy - face zero is a dummy - area zero is a dummy - node zero is a dummy */ openarena_0.8.8.orig/code/botlib/be_ai_weight.h0000644000175000017500000000553511656310264020202 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_weight.h * * desc: fuzzy weights * * $Archive: /source/code/botlib/be_ai_weight.h $ * *****************************************************************************/ #define WT_BALANCE 1 #define MAX_WEIGHTS 128 //fuzzy seperator typedef struct fuzzyseperator_s { int index; int value; int type; float weight; float minweight; float maxweight; struct fuzzyseperator_s *child; struct fuzzyseperator_s *next; } fuzzyseperator_t; //fuzzy weight typedef struct weight_s { char *name; struct fuzzyseperator_s *firstseperator; } weight_t; //weight configuration typedef struct weightconfig_s { int numweights; weight_t weights[MAX_WEIGHTS]; char filename[MAX_QPATH]; } weightconfig_t; //reads a weight configuration weightconfig_t *ReadWeightConfig(char *filename); //free a weight configuration void FreeWeightConfig(weightconfig_t *config); //writes a weight configuration, returns true if successfull qboolean WriteWeightConfig(char *filename, weightconfig_t *config); //find the fuzzy weight with the given name int FindFuzzyWeight(weightconfig_t *wc, char *name); //returns the fuzzy weight for the given inventory and weight float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum); float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum); //scales the weight with the given name void ScaleWeight(weightconfig_t *config, char *name, float scale); //scale the balance range void ScaleBalanceRange(weightconfig_t *config, float scale); //evolves the weight configuration void EvolveWeightConfig(weightconfig_t *config); //interbreed the weight configurations and stores the interbreeded one in configout void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout); //frees cached weight configurations void BotShutdownWeights(void); openarena_0.8.8.orig/code/botlib/l_precomp.h0000644000175000017500000001406511656310264017552 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_precomp.h * * desc: pre compiler * * $Archive: /source/code/botlib/l_precomp.h $ * *****************************************************************************/ #ifndef MAX_PATH #define MAX_PATH MAX_QPATH #endif #ifndef PATH_SEPERATORSTR #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) #define PATHSEPERATOR_STR "\\" #else #define PATHSEPERATOR_STR "/" #endif #endif #ifndef PATH_SEPERATORCHAR #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) #define PATHSEPERATOR_CHAR '\\' #else #define PATHSEPERATOR_CHAR '/' #endif #endif #if defined(BSPC) && !defined(QDECL) #define QDECL #endif #define DEFINE_FIXED 0x0001 #define BUILTIN_LINE 1 #define BUILTIN_FILE 2 #define BUILTIN_DATE 3 #define BUILTIN_TIME 4 #define BUILTIN_STDC 5 #define INDENT_IF 0x0001 #define INDENT_ELSE 0x0002 #define INDENT_ELIF 0x0004 #define INDENT_IFDEF 0x0008 #define INDENT_IFNDEF 0x0010 //macro definitions typedef struct define_s { char *name; //define name int flags; //define flags int builtin; // > 0 if builtin define int numparms; //number of define parameters token_t *parms; //define parameters token_t *tokens; //macro tokens (possibly containing parm tokens) struct define_s *next; //next defined macro in a list struct define_s *hashnext; //next define in the hash chain } define_t; //indents //used for conditional compilation directives: //#if, #else, #elif, #ifdef, #ifndef typedef struct indent_s { int type; //indent type int skip; //true if skipping current indent script_t *script; //script the indent was in struct indent_s *next; //next indent on the indent stack } indent_t; //source file typedef struct source_s { char filename[1024]; //file name of the script char includepath[1024]; //path to include files punctuation_t *punctuations; //punctuations to use script_t *scriptstack; //stack with scripts of the source token_t *tokens; //tokens to read first define_t *defines; //list with macro definitions define_t **definehash; //hash chain with defines indent_t *indentstack; //stack with indents int skip; // > 0 if skipping conditional code token_t token; //last read token } source_t; //read a token from the source int PC_ReadToken(source_t *source, token_t *token); //expect a certain token int PC_ExpectTokenString(source_t *source, char *string); //expect a certain token type int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token); //expect a token int PC_ExpectAnyToken(source_t *source, token_t *token); //returns true when the token is available int PC_CheckTokenString(source_t *source, char *string); //returns true an reads the token when a token with the given type is available int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token); //skip tokens until the given token string is read int PC_SkipUntilString(source_t *source, char *string); //unread the last token read from the script void PC_UnreadLastToken(source_t *source); //unread the given token void PC_UnreadToken(source_t *source, token_t *token); //read a token only if on the same line, lines are concatenated with a slash int PC_ReadLine(source_t *source, token_t *token); //returns true if there was a white space in front of the token int PC_WhiteSpaceBeforeToken(token_t *token); //add a define to the source int PC_AddDefine(source_t *source, char *string); //add a globals define that will be added to all opened sources int PC_AddGlobalDefine(char *string); //remove the given global define int PC_RemoveGlobalDefine(char *name); //remove all globals defines void PC_RemoveAllGlobalDefines(void); //add builtin defines void PC_AddBuiltinDefines(source_t *source); //set the source include path void PC_SetIncludePath(source_t *source, char *path); //set the punction set void PC_SetPunctuations(source_t *source, punctuation_t *p); //set the base folder to load files from void PC_SetBaseFolder(char *path); //load a source file source_t *LoadSourceFile(const char *filename); //load a source from memory source_t *LoadSourceMemory(char *ptr, int length, char *name); //free the given source void FreeSource(source_t *source); //print a source error void QDECL SourceError(source_t *source, char *str, ...); //print a source warning void QDECL SourceWarning(source_t *source, char *str, ...); #ifdef BSPC // some of BSPC source does include game/q_shared.h and some does not // we define pc_token_s pc_token_t if needed (yes, it's ugly) #ifndef __Q_SHARED_H #define MAX_TOKENLENGTH 1024 typedef struct pc_token_s { int type; int subtype; int intvalue; float floatvalue; char string[MAX_TOKENLENGTH]; } pc_token_t; #endif //!_Q_SHARED_H #endif //BSPC // int PC_LoadSourceHandle(const char *filename); int PC_FreeSourceHandle(int handle); int PC_ReadTokenHandle(int handle, pc_token_t *pc_token); int PC_SourceFileAndLine(int handle, char *filename, int *line); void PC_CheckOpenSourceHandles(void); openarena_0.8.8.orig/code/q3_ui/0000755000175000017500000000000011720470203015150 5ustar smcvsmcvopenarena_0.8.8.orig/code/q3_ui/ui_confirm.c0000644000175000017500000001521211656310261017454 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= CONFIRMATION MENU ======================================================================= */ #include "ui_local.h" #define ART_CONFIRM_FRAME "menu/art_blueish/cut_frame" #define ID_CONFIRM_NO 10 #define ID_CONFIRM_YES 11 typedef struct { menuframework_s menu; menutext_s no; menutext_s yes; int slashX; const char * question; void (*draw)( void ); void (*action)( qboolean result ); int style; const char **lines; } confirmMenu_t; static confirmMenu_t s_confirm; /* ================= ConfirmMenu_Event ================= */ static void ConfirmMenu_Event( void* ptr, int event ) { qboolean result; if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); if( ((menucommon_s*)ptr)->id == ID_CONFIRM_NO ) { result = qfalse; } else { result = qtrue; } if( s_confirm.action ) { s_confirm.action( result ); } } /* ================= ConfirmMenu_Key ================= */ static sfxHandle_t ConfirmMenu_Key( int key ) { switch ( key ) { case K_KP_LEFTARROW: case K_LEFTARROW: case K_KP_RIGHTARROW: case K_RIGHTARROW: key = K_TAB; break; case 'n': case 'N': ConfirmMenu_Event( &s_confirm.no, QM_ACTIVATED ); break; case 'y': case 'Y': ConfirmMenu_Event( &s_confirm.yes, QM_ACTIVATED ); break; } return Menu_DefaultKey( &s_confirm.menu, key ); } /* ================= MessaheMenu_Draw ================= */ static void MessageMenu_Draw( void ) { int i,y; UI_DrawNamedPic( 142, 118, 359, 256, ART_CONFIRM_FRAME ); y = 188; for(i=0; s_confirm.lines[i]; i++) { UI_DrawProportionalString( 320, y, s_confirm.lines[i], s_confirm.style, color_red ); y += 18; } Menu_Draw( &s_confirm.menu ); if( s_confirm.draw ) { s_confirm.draw(); } } /* ================= ConfirmMenu_Draw ================= */ static void ConfirmMenu_Draw( void ) { UI_DrawNamedPic( 142, 118, 359, 256, ART_CONFIRM_FRAME ); UI_DrawProportionalString( 320, 204, s_confirm.question, s_confirm.style, color_red ); UI_DrawProportionalString( s_confirm.slashX, 265, "/", UI_LEFT|UI_INVERSE, color_red ); Menu_Draw( &s_confirm.menu ); if( s_confirm.draw ) { s_confirm.draw(); } } /* ================= ConfirmMenu_Cache ================= */ void ConfirmMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_CONFIRM_FRAME ); } /* ================= UI_ConfirmMenu_Stlye ================= */ void UI_ConfirmMenu_Style( const char *question, int style, void (*draw)( void ), void (*action)( qboolean result ) ) { uiClientState_t cstate; int n1, n2, n3; int l1, l2, l3; // zero set all our globals memset( &s_confirm, 0, sizeof(s_confirm) ); ConfirmMenu_Cache(); n1 = UI_ProportionalStringWidth( "YES/NO" ); n2 = UI_ProportionalStringWidth( "YES" ) + PROP_GAP_WIDTH; n3 = UI_ProportionalStringWidth( "/" ) + PROP_GAP_WIDTH; l1 = 320 - ( n1 / 2 ); l2 = l1 + n2; l3 = l2 + n3; s_confirm.slashX = l2; s_confirm.question = question; s_confirm.draw = draw; s_confirm.action = action; s_confirm.style = style; s_confirm.menu.draw = ConfirmMenu_Draw; s_confirm.menu.key = ConfirmMenu_Key; s_confirm.menu.wrapAround = qtrue; trap_GetClientState( &cstate ); if ( cstate.connState >= CA_CONNECTED ) { s_confirm.menu.fullscreen = qfalse; } else { s_confirm.menu.fullscreen = qtrue; } s_confirm.yes.generic.type = MTYPE_PTEXT; s_confirm.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_confirm.yes.generic.callback = ConfirmMenu_Event; s_confirm.yes.generic.id = ID_CONFIRM_YES; s_confirm.yes.generic.x = l1; s_confirm.yes.generic.y = 264; s_confirm.yes.string = "YES"; s_confirm.yes.color = color_red; s_confirm.yes.style = UI_LEFT; s_confirm.no.generic.type = MTYPE_PTEXT; s_confirm.no.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_confirm.no.generic.callback = ConfirmMenu_Event; s_confirm.no.generic.id = ID_CONFIRM_NO; s_confirm.no.generic.x = l3; s_confirm.no.generic.y = 264; s_confirm.no.string = "NO"; s_confirm.no.color = color_red; s_confirm.no.style = UI_LEFT; Menu_AddItem( &s_confirm.menu, &s_confirm.yes ); Menu_AddItem( &s_confirm.menu, &s_confirm.no ); UI_PushMenu( &s_confirm.menu ); Menu_SetCursorToItem( &s_confirm.menu, &s_confirm.no ); } /* ================= UI_ConfirmMenu ================= */ void UI_ConfirmMenu( const char *question, void (*draw)( void ), void (*action)( qboolean result ) ) { UI_ConfirmMenu_Style(question, UI_CENTER|UI_INVERSE, draw, action); } /* ================= UI_Message hacked over from Confirm stuff ================= */ void UI_Message( const char **lines ) { uiClientState_t cstate; int n1, l1; // zero set all our globals memset( &s_confirm, 0, sizeof(s_confirm) ); ConfirmMenu_Cache(); n1 = UI_ProportionalStringWidth( "OK" ); l1 = 320 - ( n1 / 2 ); s_confirm.lines = lines; s_confirm.style = UI_CENTER|UI_INVERSE|UI_SMALLFONT; s_confirm.menu.draw = MessageMenu_Draw; s_confirm.menu.key = ConfirmMenu_Key; s_confirm.menu.wrapAround = qtrue; trap_GetClientState( &cstate ); if ( cstate.connState >= CA_CONNECTED ) { s_confirm.menu.fullscreen = qfalse; } else { s_confirm.menu.fullscreen = qtrue; } s_confirm.yes.generic.type = MTYPE_PTEXT; s_confirm.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_confirm.yes.generic.callback = ConfirmMenu_Event; s_confirm.yes.generic.id = ID_CONFIRM_YES; s_confirm.yes.generic.x = l1; s_confirm.yes.generic.y = 280; s_confirm.yes.string = "OK"; s_confirm.yes.color = color_red; s_confirm.yes.style = UI_LEFT; Menu_AddItem( &s_confirm.menu, &s_confirm.yes ); UI_PushMenu( &s_confirm.menu ); Menu_SetCursorToItem( &s_confirm.menu, &s_confirm.yes ); } openarena_0.8.8.orig/code/q3_ui/ui_video.c0000644000175000017500000011652611656310261017137 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" void GraphicsOptions_MenuInit( void ); /* ======================================================================= DRIVER INFORMATION MENU ======================================================================= */ #define DRIVERINFO_FRAMEL "menu/art_blueish/frame2_l" #define DRIVERINFO_FRAMER "menu/art_blueish/frame1_r" #define DRIVERINFO_BACK0 "menu/art_blueish/back_0" #define DRIVERINFO_BACK1 "menu/art_blueish/back_1" static char* driverinfo_artlist[] = { DRIVERINFO_FRAMEL, DRIVERINFO_FRAMER, DRIVERINFO_BACK0, DRIVERINFO_BACK1, NULL, }; #define ID_DRIVERINFOBACK 100 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s framel; menubitmap_s framer; char stringbuff[1024]; char* strings[64]; int numstrings; } driverinfo_t; static driverinfo_t s_driverinfo; /* ================= DriverInfo_Event ================= */ static void DriverInfo_Event( void* ptr, int event ) { if (event != QM_ACTIVATED) return; switch (((menucommon_s*)ptr)->id) { case ID_DRIVERINFOBACK: UI_PopMenu(); break; } } /* ================= DriverInfo_MenuDraw ================= */ static void DriverInfo_MenuDraw( void ) { int i; int y; Menu_Draw( &s_driverinfo.menu ); UI_DrawString( 320, 80, "VENDOR", UI_CENTER|UI_SMALLFONT, color_red ); UI_DrawString( 320, 152, "PIXELFORMAT", UI_CENTER|UI_SMALLFONT, color_red ); UI_DrawString( 320, 192, "EXTENSIONS", UI_CENTER|UI_SMALLFONT, color_red ); UI_DrawString( 320, 80+16, uis.glconfig.vendor_string, UI_CENTER|UI_SMALLFONT, text_color_normal ); UI_DrawString( 320, 96+16, uis.glconfig.version_string, UI_CENTER|UI_SMALLFONT, text_color_normal ); UI_DrawString( 320, 112+16, uis.glconfig.renderer_string, UI_CENTER|UI_SMALLFONT, text_color_normal ); UI_DrawString( 320, 152+16, va ("color(%d-bits) Z(%d-bits) stencil(%d-bits)", uis.glconfig.colorBits, uis.glconfig.depthBits, uis.glconfig.stencilBits), UI_CENTER|UI_SMALLFONT, text_color_normal ); // double column y = 192+16; for (i=0; i 32) { s_driverinfo.strings[i][len-1] = '>'; s_driverinfo.strings[i][len] = '\0'; } } Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.banner ); Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.framel ); Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.framer ); Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.back ); UI_PushMenu( &s_driverinfo.menu ); } /* ======================================================================= GRAPHICS OPTIONS MENU ======================================================================= */ #define GRAPHICSOPTIONS_FRAMEL "menu/art_blueish/frame2_l" #define GRAPHICSOPTIONS_FRAMER "menu/art_blueish/frame1_r" #define GRAPHICSOPTIONS_BACK0 "menu/art_blueish/back_0" #define GRAPHICSOPTIONS_BACK1 "menu/art_blueish/back_1" #define GRAPHICSOPTIONS_ACCEPT0 "menu/art_blueish/accept_0" #define GRAPHICSOPTIONS_ACCEPT1 "menu/art_blueish/accept_1" #define ID_BACK2 101 #define ID_FULLSCREEN 102 #define ID_LIST 103 #define ID_MODE 104 #define ID_DRIVERINFO 105 #define ID_GRAPHICS 106 #define ID_DISPLAY 107 #define ID_SOUND 108 #define ID_NETWORK 109 #define ID_RATIO 110 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s graphics; menutext_s display; menutext_s sound; menutext_s network; menulist_s list; menulist_s ratio; menulist_s mode; menulist_s driver; menuslider_s tq; menulist_s fs; menulist_s lighting; menulist_s flares; menulist_s bloom; menulist_s allow_extensions; menulist_s texturebits; menulist_s geometry; menulist_s filter; menulist_s aniso; menutext_s driverinfo; menubitmap_s apply; menubitmap_s back; } graphicsoptions_t; typedef struct { int mode; qboolean fullscreen; int tq; int lighting; qboolean flares; qboolean bloom; int texturebits; int geometry; int filter; int aniso; int driver; qboolean extensions; } InitialVideoOptions_s; static InitialVideoOptions_s s_ivo; static graphicsoptions_t s_graphicsoptions; static InitialVideoOptions_s s_ivo_templates[] = { { 6, qtrue, 3, 0, qfalse,qfalse, 2, 2, 1, 0, 0, qtrue }, { 4, qtrue, 2, 0, qfalse,qfalse, 2, 1, 1, 0, 0, qtrue // JDC: this was tq 3 }, { 3, qtrue, 2, 0, qfalse,qfalse, 0, 1, 0, 0, 0, qtrue }, { 2, qtrue, 1, 0, qfalse,qfalse, 0, 0, 0, 0, 0, qtrue }, { 2, qtrue, 1, 1, qfalse,qfalse, 0, 0, 0, 0, 0, qtrue }, { 3, qtrue, 1, 0, qfalse,qfalse, 0, 1, 0, 0, 0, qtrue } }; #define NUM_IVO_TEMPLATES ( sizeof( s_ivo_templates ) / sizeof( s_ivo_templates[0] ) ) static const char *builtinResolutions[ ] = { "320x240", "400x300", "512x384", "640x480", "800x600", "960x720", "1024x768", "1152x864", "1280x1024", "1600x1200", "2048x1536", "856x480", NULL }; static const char *knownRatios[ ][2] = { { "1.25:1", "5:4" }, { "1.33:1", "4:3" }, { "1.50:1", "3:2" }, { "1.56:1", "14:9" }, { "1.60:1", "16:10" }, { "1.67:1", "5:3" }, { "1.78:1", "16:9" }, { NULL , NULL } }; #define MAX_RESOLUTIONS 32 static const char* ratios[ MAX_RESOLUTIONS ]; static char ratioBuf[ MAX_RESOLUTIONS ][ 8 ]; static int ratioToRes[ MAX_RESOLUTIONS ]; static int resToRatio[ MAX_RESOLUTIONS ]; static char resbuf[ MAX_STRING_CHARS ]; static const char* detectedResolutions[ MAX_RESOLUTIONS ]; static const char** resolutions = builtinResolutions; static qboolean resolutionsDetected = qfalse; /* ================= GraphicsOptions_FindBuiltinResolution ================= */ static int GraphicsOptions_FindBuiltinResolution( int mode ) { int i; if( !resolutionsDetected ) return mode; if( mode < 0 ) return -1; for( i = 0; builtinResolutions[ i ]; i++ ) { if( !Q_stricmp( builtinResolutions[ i ], detectedResolutions[ mode ] ) ) return i; } return -1; } /* ================= GraphicsOptions_FindDetectedResolution ================= */ static int GraphicsOptions_FindDetectedResolution( int mode ) { int i; if( !resolutionsDetected ) return mode; if( mode < 0 ) return -1; for( i = 0; detectedResolutions[ i ]; i++ ) { if( !Q_stricmp( builtinResolutions[ mode ], detectedResolutions[ i ] ) ) return i; } return -1; } /* ================= GraphicsOptions_GetAspectRatios ================= */ static void GraphicsOptions_GetAspectRatios( void ) { int i, r; // build ratio list from resolutions for( r = 0; resolutions[r]; r++ ) { int w, h; char *x; char str[ sizeof(ratioBuf[0]) ]; // calculate resolution's aspect ratio x = strchr( resolutions[r], 'x' )+1; Q_strncpyz( str, resolutions[r], x-resolutions[r] ); w = atoi( str ); h = atoi( x ); Com_sprintf( str, sizeof(str), "%.2f:1", (float)w / (float)h ); // rename common ratios ("1.33:1" -> "4:3") for( i = 0; knownRatios[i][0]; i++ ) { if( !Q_stricmp( str, knownRatios[i][0] ) ) { Q_strncpyz( str, knownRatios[i][1], sizeof( str ) ); break; } } // add ratio to list if it is new // establish res/ratio relationship for( i = 0; ratioBuf[i][0]; i++ ) { if( !Q_stricmp( str, ratioBuf[i] ) ) break; } if( !ratioBuf[i][0] ) { Q_strncpyz( ratioBuf[i], str, sizeof(ratioBuf[i]) ); ratioToRes[i] = r; } ratios[r] = ratioBuf[r]; resToRatio[r] = i; } ratios[r] = NULL; } /* ================= GraphicsOptions_GetInitialVideo ================= */ static void GraphicsOptions_GetInitialVideo( void ) { s_ivo.driver = s_graphicsoptions.driver.curvalue; s_ivo.mode = s_graphicsoptions.mode.curvalue; s_ivo.fullscreen = s_graphicsoptions.fs.curvalue; s_ivo.extensions = s_graphicsoptions.allow_extensions.curvalue; s_ivo.tq = s_graphicsoptions.tq.curvalue; s_ivo.lighting = s_graphicsoptions.lighting.curvalue; s_ivo.flares = s_graphicsoptions.flares.curvalue; s_ivo.bloom = s_graphicsoptions.bloom.curvalue; s_ivo.geometry = s_graphicsoptions.geometry.curvalue; s_ivo.filter = s_graphicsoptions.filter.curvalue; s_ivo.aniso = s_graphicsoptions.aniso.curvalue; s_ivo.texturebits = s_graphicsoptions.texturebits.curvalue; } /* ================= GraphicsOptions_GetResolutions ================= */ static void GraphicsOptions_GetResolutions( void ) { Q_strncpyz(resbuf, UI_Cvar_VariableString("r_availableModes"), sizeof(resbuf)); if(*resbuf) { char* s = resbuf; unsigned int i = 0; while( s && i < sizeof(detectedResolutions)/sizeof(detectedResolutions[0])-1) { detectedResolutions[i++] = s; s = strchr(s, ' '); if( s ) *s++ = '\0'; } detectedResolutions[ i ] = NULL; if( i > 0 ) { resolutions = detectedResolutions; resolutionsDetected = qtrue; } } } /* ================= GraphicsOptions_CheckConfig ================= */ static void GraphicsOptions_CheckConfig( void ) { int i; for ( i = 0; i < NUM_IVO_TEMPLATES-1; i++ ) { if ( s_ivo_templates[i].driver != s_graphicsoptions.driver.curvalue ) continue; if ( GraphicsOptions_FindDetectedResolution(s_ivo_templates[i].mode) != s_graphicsoptions.mode.curvalue ) continue; if ( s_ivo_templates[i].fullscreen != s_graphicsoptions.fs.curvalue ) continue; if ( s_ivo_templates[i].tq != s_graphicsoptions.tq.curvalue ) continue; if ( s_ivo_templates[i].lighting != s_graphicsoptions.lighting.curvalue ) continue; if ( s_ivo_templates[i].flares != s_graphicsoptions.flares.curvalue ) continue; if ( s_ivo_templates[i].bloom != s_graphicsoptions.bloom.curvalue ) continue; if ( s_ivo_templates[i].geometry != s_graphicsoptions.geometry.curvalue ) continue; if ( s_ivo_templates[i].filter != s_graphicsoptions.filter.curvalue ) continue; if ( s_ivo_templates[i].aniso != s_graphicsoptions.aniso.curvalue ) continue; // if ( s_ivo_templates[i].texturebits != s_graphicsoptions.texturebits.curvalue ) // continue; s_graphicsoptions.list.curvalue = i; return; } // return 'Custom' ivo template s_graphicsoptions.list.curvalue = NUM_IVO_TEMPLATES - 1; } /* ================= GraphicsOptions_UpdateMenuItems ================= */ static void GraphicsOptions_UpdateMenuItems( void ) { if ( s_graphicsoptions.driver.curvalue == 1 ) { s_graphicsoptions.fs.curvalue = 1; s_graphicsoptions.fs.generic.flags |= QMF_GRAYED; } else { s_graphicsoptions.fs.generic.flags &= ~QMF_GRAYED; } if ( s_graphicsoptions.allow_extensions.curvalue == 0 ) { if ( s_graphicsoptions.texturebits.curvalue == 0 ) { s_graphicsoptions.texturebits.curvalue = 1; } } s_graphicsoptions.apply.generic.flags |= QMF_HIDDEN|QMF_INACTIVE; if ( s_ivo.mode != s_graphicsoptions.mode.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.fullscreen != s_graphicsoptions.fs.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.extensions != s_graphicsoptions.allow_extensions.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.tq != s_graphicsoptions.tq.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.lighting != s_graphicsoptions.lighting.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.flares != s_graphicsoptions.flares.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.bloom != s_graphicsoptions.bloom.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.driver != s_graphicsoptions.driver.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.texturebits != s_graphicsoptions.texturebits.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.geometry != s_graphicsoptions.geometry.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.filter != s_graphicsoptions.filter.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } if ( s_ivo.aniso != s_graphicsoptions.aniso.curvalue ) { s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE); } GraphicsOptions_CheckConfig(); } /* ================= GraphicsOptions_ApplyChanges ================= */ static void GraphicsOptions_ApplyChanges( void *unused, int notification ) { if (notification != QM_ACTIVATED) return; switch ( s_graphicsoptions.texturebits.curvalue ) { case 0: trap_Cvar_SetValue( "r_texturebits", 0 ); break; case 1: trap_Cvar_SetValue( "r_texturebits", 16 ); break; case 2: trap_Cvar_SetValue( "r_texturebits", 32 ); break; } trap_Cvar_SetValue( "r_picmip", 3 - s_graphicsoptions.tq.curvalue ); trap_Cvar_SetValue( "r_allowExtensions", s_graphicsoptions.allow_extensions.curvalue ); if( resolutionsDetected ) { // search for builtin mode that matches the detected mode int mode; if ( s_graphicsoptions.mode.curvalue == -1 || s_graphicsoptions.mode.curvalue >= sizeof(detectedResolutions)/sizeof(detectedResolutions[0]) ) s_graphicsoptions.mode.curvalue = 0; mode = GraphicsOptions_FindBuiltinResolution( s_graphicsoptions.mode.curvalue ); if( mode == -1 ) { char w[ 16 ], h[ 16 ]; Q_strncpyz( w, detectedResolutions[ s_graphicsoptions.mode.curvalue ], sizeof( w ) ); *strchr( w, 'x' ) = 0; Q_strncpyz( h, strchr( detectedResolutions[ s_graphicsoptions.mode.curvalue ], 'x' ) + 1, sizeof( h ) ); trap_Cvar_Set( "r_customwidth", w ); trap_Cvar_Set( "r_customheight", h ); } trap_Cvar_SetValue( "r_mode", mode ); } else trap_Cvar_SetValue( "r_mode", s_graphicsoptions.mode.curvalue ); trap_Cvar_SetValue( "r_fullscreen", s_graphicsoptions.fs.curvalue ); trap_Cvar_SetValue( "r_colorbits", 0 ); trap_Cvar_SetValue( "r_depthbits", 0 ); trap_Cvar_SetValue( "r_stencilbits", 0 ); trap_Cvar_SetValue( "r_vertexLight", s_graphicsoptions.lighting.curvalue ); trap_Cvar_SetValue( "cg_autovertex", s_graphicsoptions.lighting.curvalue ); trap_Cvar_SetValue( "r_flares", s_graphicsoptions.flares.curvalue ); trap_Cvar_SetValue( "r_bloom", s_graphicsoptions.bloom.curvalue ); //r_ext_texture_filter_anisotropic is special if(s_graphicsoptions.aniso.curvalue) { trap_Cvar_SetValue( "r_ext_max_anisotropy", s_graphicsoptions.aniso.curvalue*2 ); trap_Cvar_SetValue( "r_ext_texture_filter_anisotropic", qtrue ); } else trap_Cvar_SetValue( "r_ext_texture_filter_anisotropic", qfalse ); trap_Cvar_SetValue( "com_hunkmegs", 128 ); if ( s_graphicsoptions.geometry.curvalue == 2 ) { trap_Cvar_SetValue( "r_lodBias", 0 ); trap_Cvar_SetValue( "r_subdivisions", 4 ); } else if ( s_graphicsoptions.geometry.curvalue == 1 ) { trap_Cvar_SetValue( "r_lodBias", 1 ); trap_Cvar_SetValue( "r_subdivisions", 12 ); } else { trap_Cvar_SetValue( "r_lodBias", 1 ); trap_Cvar_SetValue( "r_subdivisions", 20 ); } if ( s_graphicsoptions.filter.curvalue ) { trap_Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_LINEAR" ); } else { trap_Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); } trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" ); } /* ================= GraphicsOptions_Event ================= */ static void GraphicsOptions_Event( void* ptr, int event ) { InitialVideoOptions_s *ivo; if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_RATIO: s_graphicsoptions.mode.curvalue = ratioToRes[ s_graphicsoptions.ratio.curvalue ]; // fall through to apply mode constraints case ID_MODE: // clamp 3dfx video modes if ( s_graphicsoptions.driver.curvalue == 1 ) { if ( s_graphicsoptions.mode.curvalue < 2 ) s_graphicsoptions.mode.curvalue = 2; else if ( s_graphicsoptions.mode.curvalue > 6 ) s_graphicsoptions.mode.curvalue = 6; } s_graphicsoptions.ratio.curvalue = resToRatio[ s_graphicsoptions.mode.curvalue ]; break; case ID_LIST: ivo = &s_ivo_templates[s_graphicsoptions.list.curvalue]; s_graphicsoptions.mode.curvalue = GraphicsOptions_FindDetectedResolution(ivo->mode); s_graphicsoptions.ratio.curvalue = resToRatio[ s_graphicsoptions.mode.curvalue ]; s_graphicsoptions.tq.curvalue = ivo->tq; s_graphicsoptions.lighting.curvalue = ivo->lighting; s_graphicsoptions.texturebits.curvalue = ivo->texturebits; s_graphicsoptions.geometry.curvalue = ivo->geometry; s_graphicsoptions.filter.curvalue = ivo->filter; s_graphicsoptions.aniso.curvalue = ivo->aniso; s_graphicsoptions.fs.curvalue = ivo->fullscreen; s_graphicsoptions.flares.curvalue = ivo->flares; s_graphicsoptions.bloom.curvalue = ivo->bloom; break; case ID_DRIVERINFO: UI_DriverInfo_Menu(); break; case ID_BACK2: UI_PopMenu(); break; case ID_GRAPHICS: break; case ID_DISPLAY: UI_PopMenu(); UI_DisplayOptionsMenu(); break; case ID_SOUND: UI_PopMenu(); UI_SoundOptionsMenu(); break; case ID_NETWORK: UI_PopMenu(); UI_NetworkOptionsMenu(); break; } } /* ================ GraphicsOptions_TQEvent ================ */ static void GraphicsOptions_TQEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } s_graphicsoptions.tq.curvalue = (int)(s_graphicsoptions.tq.curvalue + 0.5); } /* ================ GraphicsOptions_MenuDraw ================ */ void GraphicsOptions_MenuDraw (void) { //APSFIX - rework this GraphicsOptions_UpdateMenuItems(); Menu_Draw( &s_graphicsoptions.menu ); } /* ================= GraphicsOptions_SetMenuItems ================= */ static void GraphicsOptions_SetMenuItems( void ) { s_graphicsoptions.mode.curvalue = GraphicsOptions_FindDetectedResolution( trap_Cvar_VariableValue( "r_mode" ) ); if ( s_graphicsoptions.mode.curvalue < 0 ) { if( resolutionsDetected ) { int i; char buf[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer("r_customwidth", buf, sizeof(buf)-2); buf[strlen(buf)+1] = 0; buf[strlen(buf)] = 'x'; trap_Cvar_VariableStringBuffer("r_customheight", buf+strlen(buf), sizeof(buf)-strlen(buf)); for(i = 0; detectedResolutions[i]; ++i) { if(!Q_stricmp(buf, detectedResolutions[i])) { s_graphicsoptions.mode.curvalue = i; break; } } if ( s_graphicsoptions.mode.curvalue < 0 ) s_graphicsoptions.mode.curvalue = 0; } else { s_graphicsoptions.mode.curvalue = 3; } } s_graphicsoptions.fs.curvalue = trap_Cvar_VariableValue("r_fullscreen"); s_graphicsoptions.allow_extensions.curvalue = trap_Cvar_VariableValue("r_allowExtensions"); s_graphicsoptions.flares.curvalue = trap_Cvar_VariableValue("r_flares"); s_graphicsoptions.bloom.curvalue = trap_Cvar_VariableValue("r_bloom"); if(trap_Cvar_VariableValue("r_ext_texture_filter_anisotropic")) { s_graphicsoptions.aniso.curvalue = trap_Cvar_VariableValue("r_ext_max_anisotropy")/2; } s_graphicsoptions.tq.curvalue = 3-trap_Cvar_VariableValue( "r_picmip"); if ( s_graphicsoptions.tq.curvalue < 0 ) { s_graphicsoptions.tq.curvalue = 0; } else if ( s_graphicsoptions.tq.curvalue > 3 ) { s_graphicsoptions.tq.curvalue = 3; } s_graphicsoptions.lighting.curvalue = trap_Cvar_VariableValue( "r_vertexLight" ) != 0; switch ( ( int ) trap_Cvar_VariableValue( "r_texturebits" ) ) { default: case 0: s_graphicsoptions.texturebits.curvalue = 0; break; case 16: s_graphicsoptions.texturebits.curvalue = 1; break; case 32: s_graphicsoptions.texturebits.curvalue = 2; break; } if ( !Q_stricmp( UI_Cvar_VariableString( "r_textureMode" ), "GL_LINEAR_MIPMAP_NEAREST" ) ) { s_graphicsoptions.filter.curvalue = 0; } else { s_graphicsoptions.filter.curvalue = 1; } if ( trap_Cvar_VariableValue( "r_lodBias" ) > 0 ) { if ( trap_Cvar_VariableValue( "r_subdivisions" ) >= 20 ) { s_graphicsoptions.geometry.curvalue = 0; } else { s_graphicsoptions.geometry.curvalue = 1; } } else { s_graphicsoptions.geometry.curvalue = 2; } } /* ================ GraphicsOptions_MenuInit ================ */ void GraphicsOptions_MenuInit( void ) { static const char *s_driver_names[] = { "Default", "Voodoo", NULL }; static const char *tq_names[] = { "Default", "16 bit", "32 bit", NULL }; static const char *s_graphics_options_names[] = { "Very High Quality", "High Quality", "Normal", "Fast", "Fastest", "Custom", NULL }; static const char *lighting_names[] = { "Lightmap (Normal)", "Vertex (Low)", NULL }; static const char *filter_names[] = { "Bilinear", "Trilinear", NULL }; static const char *aniso_names[] = { "Off", "2x", "4x", "6x", "8x", NULL }; static const char *quality_names[] = { "Low", "Medium", "High", NULL }; static const char *enabled_names[] = { "Off", "On", NULL }; int y; // zero set all our globals memset( &s_graphicsoptions, 0 ,sizeof(graphicsoptions_t) ); GraphicsOptions_GetResolutions(); GraphicsOptions_GetAspectRatios(); GraphicsOptions_Cache(); s_graphicsoptions.menu.wrapAround = qtrue; s_graphicsoptions.menu.fullscreen = qtrue; s_graphicsoptions.menu.draw = GraphicsOptions_MenuDraw; s_graphicsoptions.banner.generic.type = MTYPE_BTEXT; s_graphicsoptions.banner.generic.x = 320; s_graphicsoptions.banner.generic.y = 16; s_graphicsoptions.banner.string = "SYSTEM SETUP"; s_graphicsoptions.banner.color = color_white; s_graphicsoptions.banner.style = UI_CENTER; s_graphicsoptions.framel.generic.type = MTYPE_BITMAP; s_graphicsoptions.framel.generic.name = GRAPHICSOPTIONS_FRAMEL; s_graphicsoptions.framel.generic.flags = QMF_INACTIVE; s_graphicsoptions.framel.generic.x = 0; s_graphicsoptions.framel.generic.y = 78; s_graphicsoptions.framel.width = 256; s_graphicsoptions.framel.height = 329; s_graphicsoptions.framer.generic.type = MTYPE_BITMAP; s_graphicsoptions.framer.generic.name = GRAPHICSOPTIONS_FRAMER; s_graphicsoptions.framer.generic.flags = QMF_INACTIVE; s_graphicsoptions.framer.generic.x = 376; s_graphicsoptions.framer.generic.y = 76; s_graphicsoptions.framer.width = 256; s_graphicsoptions.framer.height = 334; s_graphicsoptions.graphics.generic.type = MTYPE_PTEXT; s_graphicsoptions.graphics.generic.flags = QMF_RIGHT_JUSTIFY; s_graphicsoptions.graphics.generic.id = ID_GRAPHICS; s_graphicsoptions.graphics.generic.callback = GraphicsOptions_Event; s_graphicsoptions.graphics.generic.x = 216; s_graphicsoptions.graphics.generic.y = 240 - 2 * PROP_HEIGHT; s_graphicsoptions.graphics.string = "GRAPHICS"; s_graphicsoptions.graphics.style = UI_RIGHT; s_graphicsoptions.graphics.color = color_red; s_graphicsoptions.display.generic.type = MTYPE_PTEXT; s_graphicsoptions.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_graphicsoptions.display.generic.id = ID_DISPLAY; s_graphicsoptions.display.generic.callback = GraphicsOptions_Event; s_graphicsoptions.display.generic.x = 216; s_graphicsoptions.display.generic.y = 240 - PROP_HEIGHT; s_graphicsoptions.display.string = "DISPLAY"; s_graphicsoptions.display.style = UI_RIGHT; s_graphicsoptions.display.color = color_red; s_graphicsoptions.sound.generic.type = MTYPE_PTEXT; s_graphicsoptions.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_graphicsoptions.sound.generic.id = ID_SOUND; s_graphicsoptions.sound.generic.callback = GraphicsOptions_Event; s_graphicsoptions.sound.generic.x = 216; s_graphicsoptions.sound.generic.y = 240; s_graphicsoptions.sound.string = "SOUND"; s_graphicsoptions.sound.style = UI_RIGHT; s_graphicsoptions.sound.color = color_red; s_graphicsoptions.network.generic.type = MTYPE_PTEXT; s_graphicsoptions.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_graphicsoptions.network.generic.id = ID_NETWORK; s_graphicsoptions.network.generic.callback = GraphicsOptions_Event; s_graphicsoptions.network.generic.x = 216; s_graphicsoptions.network.generic.y = 240 + PROP_HEIGHT; s_graphicsoptions.network.string = "NETWORK"; s_graphicsoptions.network.style = UI_RIGHT; s_graphicsoptions.network.color = color_red; y = 240 - 7 * (BIGCHAR_HEIGHT + 2); s_graphicsoptions.list.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.list.generic.name = "Graphics Settings:"; s_graphicsoptions.list.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.list.generic.x = 400; s_graphicsoptions.list.generic.y = y; s_graphicsoptions.list.generic.callback = GraphicsOptions_Event; s_graphicsoptions.list.generic.id = ID_LIST; s_graphicsoptions.list.itemnames = s_graphics_options_names; y += 2 * ( BIGCHAR_HEIGHT + 2 ); s_graphicsoptions.driver.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.driver.generic.name = "GL Driver:"; s_graphicsoptions.driver.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.driver.generic.x = 400; s_graphicsoptions.driver.generic.y = y; s_graphicsoptions.driver.itemnames = s_driver_names; s_graphicsoptions.driver.curvalue = (uis.glconfig.driverType == GLDRV_VOODOO); y += BIGCHAR_HEIGHT+2; // references/modifies "r_allowExtensions" s_graphicsoptions.allow_extensions.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.allow_extensions.generic.name = "GL Extensions:"; s_graphicsoptions.allow_extensions.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.allow_extensions.generic.x = 400; s_graphicsoptions.allow_extensions.generic.y = y; s_graphicsoptions.allow_extensions.itemnames = enabled_names; y += BIGCHAR_HEIGHT+2; s_graphicsoptions.ratio.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.ratio.generic.name = "Aspect Ratio:"; s_graphicsoptions.ratio.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.ratio.generic.x = 400; s_graphicsoptions.ratio.generic.y = y; s_graphicsoptions.ratio.itemnames = ratios; s_graphicsoptions.ratio.generic.callback = GraphicsOptions_Event; s_graphicsoptions.ratio.generic.id = ID_RATIO; y += BIGCHAR_HEIGHT+2; // references/modifies "r_mode" s_graphicsoptions.mode.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.mode.generic.name = "Resolution:"; s_graphicsoptions.mode.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.mode.generic.x = 400; s_graphicsoptions.mode.generic.y = y; s_graphicsoptions.mode.itemnames = resolutions; s_graphicsoptions.mode.generic.callback = GraphicsOptions_Event; s_graphicsoptions.mode.generic.id = ID_MODE; y += BIGCHAR_HEIGHT+2; // references/modifies "r_fullscreen" s_graphicsoptions.fs.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.fs.generic.name = "Fullscreen:"; s_graphicsoptions.fs.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.fs.generic.x = 400; s_graphicsoptions.fs.generic.y = y; s_graphicsoptions.fs.itemnames = enabled_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_vertexLight" s_graphicsoptions.lighting.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.lighting.generic.name = "Lighting:"; s_graphicsoptions.lighting.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.lighting.generic.x = 400; s_graphicsoptions.lighting.generic.y = y; s_graphicsoptions.lighting.itemnames = lighting_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_flares" s_graphicsoptions.flares.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.flares.generic.name = "Flares:"; s_graphicsoptions.flares.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.flares.generic.x = 400; s_graphicsoptions.flares.generic.y = y; s_graphicsoptions.flares.itemnames = enabled_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_bloom" s_graphicsoptions.bloom.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.bloom.generic.name = "Bloom:"; s_graphicsoptions.bloom.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.bloom.generic.x = 400; s_graphicsoptions.bloom.generic.y = y; s_graphicsoptions.bloom.itemnames = enabled_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_lodBias" & "subdivisions" s_graphicsoptions.geometry.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.geometry.generic.name = "Geometric Detail:"; s_graphicsoptions.geometry.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.geometry.generic.x = 400; s_graphicsoptions.geometry.generic.y = y; s_graphicsoptions.geometry.itemnames = quality_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_picmip" s_graphicsoptions.tq.generic.type = MTYPE_SLIDER; s_graphicsoptions.tq.generic.name = "Texture Detail:"; s_graphicsoptions.tq.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.tq.generic.x = 400; s_graphicsoptions.tq.generic.y = y; s_graphicsoptions.tq.minvalue = 0; s_graphicsoptions.tq.maxvalue = 3; s_graphicsoptions.tq.generic.callback = GraphicsOptions_TQEvent; y += BIGCHAR_HEIGHT+2; // references/modifies "r_textureBits" s_graphicsoptions.texturebits.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.texturebits.generic.name = "Texture Quality:"; s_graphicsoptions.texturebits.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.texturebits.generic.x = 400; s_graphicsoptions.texturebits.generic.y = y; s_graphicsoptions.texturebits.itemnames = tq_names; y += BIGCHAR_HEIGHT+2; // references/modifies "r_textureMode" s_graphicsoptions.filter.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.filter.generic.name = "Texture Filter:"; s_graphicsoptions.filter.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.filter.generic.x = 400; s_graphicsoptions.filter.generic.y = y; s_graphicsoptions.filter.itemnames = filter_names; y += 2+BIGCHAR_HEIGHT; s_graphicsoptions.aniso.generic.type = MTYPE_SPINCONTROL; s_graphicsoptions.aniso.generic.name = "Anisotropy:"; s_graphicsoptions.aniso.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_graphicsoptions.aniso.generic.x = 400; s_graphicsoptions.aniso.generic.y = y; s_graphicsoptions.aniso.itemnames = aniso_names; y += 2*BIGCHAR_HEIGHT; s_graphicsoptions.driverinfo.generic.type = MTYPE_PTEXT; s_graphicsoptions.driverinfo.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_graphicsoptions.driverinfo.generic.callback = GraphicsOptions_Event; s_graphicsoptions.driverinfo.generic.id = ID_DRIVERINFO; s_graphicsoptions.driverinfo.generic.x = 320; s_graphicsoptions.driverinfo.generic.y = y; s_graphicsoptions.driverinfo.string = "Driver Info"; s_graphicsoptions.driverinfo.style = UI_CENTER|UI_SMALLFONT; s_graphicsoptions.driverinfo.color = color_red; y += BIGCHAR_HEIGHT+2; s_graphicsoptions.back.generic.type = MTYPE_BITMAP; s_graphicsoptions.back.generic.name = GRAPHICSOPTIONS_BACK0; s_graphicsoptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_graphicsoptions.back.generic.callback = GraphicsOptions_Event; s_graphicsoptions.back.generic.id = ID_BACK2; s_graphicsoptions.back.generic.x = 0; s_graphicsoptions.back.generic.y = 480-64; s_graphicsoptions.back.width = 128; s_graphicsoptions.back.height = 64; s_graphicsoptions.back.focuspic = GRAPHICSOPTIONS_BACK1; s_graphicsoptions.apply.generic.type = MTYPE_BITMAP; s_graphicsoptions.apply.generic.name = GRAPHICSOPTIONS_ACCEPT0; s_graphicsoptions.apply.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_HIDDEN|QMF_INACTIVE; s_graphicsoptions.apply.generic.callback = GraphicsOptions_ApplyChanges; s_graphicsoptions.apply.generic.x = 640; s_graphicsoptions.apply.generic.y = 480-64; s_graphicsoptions.apply.width = 128; s_graphicsoptions.apply.height = 64; s_graphicsoptions.apply.focuspic = GRAPHICSOPTIONS_ACCEPT1; Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.banner ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.framel ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.framer ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.graphics ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.display ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.sound ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.network ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.list ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.driver ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.allow_extensions ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.ratio ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.mode ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.fs ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.lighting ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.flares ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.bloom ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.geometry ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.tq ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.texturebits ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.filter ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.aniso ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.driverinfo ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.back ); Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.apply ); GraphicsOptions_SetMenuItems(); GraphicsOptions_GetInitialVideo(); if ( uis.glconfig.driverType == GLDRV_ICD && uis.glconfig.hardwareType == GLHW_3DFX_2D3D ) { s_graphicsoptions.driver.generic.flags |= QMF_HIDDEN|QMF_INACTIVE; } } /* ================= GraphicsOptions_Cache ================= */ void GraphicsOptions_Cache( void ) { trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_FRAMEL ); trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_FRAMER ); trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_BACK0 ); trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_BACK1 ); trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_ACCEPT0 ); trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_ACCEPT1 ); } /* ================= UI_GraphicsOptionsMenu ================= */ void UI_GraphicsOptionsMenu( void ) { GraphicsOptions_MenuInit(); UI_PushMenu( &s_graphicsoptions.menu ); Menu_SetCursorToItem( &s_graphicsoptions.menu, &s_graphicsoptions.graphics ); } openarena_0.8.8.orig/code/q3_ui/ui_connect.c0000644000175000017500000002276011656310261017456 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /* =============================================================================== CONNECTION SCREEN =============================================================================== */ qboolean passwordNeeded = qtrue; menufield_s passwordField; static connstate_t lastConnState; static char lastLoadingText[MAX_INFO_VALUE]; static void UI_ReadableSize ( char *buf, int bufsize, int value ) { if (value > 1024*1024*1024 ) { // gigs Com_sprintf( buf, bufsize, "%d", value / (1024*1024*1024) ); Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d GB", (value % (1024*1024*1024))*100 / (1024*1024*1024) ); } else if (value > 1024*1024 ) { // megs Com_sprintf( buf, bufsize, "%d", value / (1024*1024) ); Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d MB", (value % (1024*1024))*100 / (1024*1024) ); } else if (value > 1024 ) { // kilos Com_sprintf( buf, bufsize, "%d KB", value / 1024 ); } else { // bytes Com_sprintf( buf, bufsize, "%d bytes", value ); } } // Assumes time is in msec static void UI_PrintTime ( char *buf, int bufsize, int time ) { time /= 1000; // change to seconds if (time > 3600) { // in the hours range Com_sprintf( buf, bufsize, "%d hr %d min", time / 3600, (time % 3600) / 60 ); } else if (time > 60) { // mins Com_sprintf( buf, bufsize, "%d min %d sec", time / 60, time % 60 ); } else { // secs Com_sprintf( buf, bufsize, "%d sec", time ); } } static void UI_DisplayDownloadInfo( const char *downloadName ) { static char dlText[] = "Downloading:"; static char etaText[] = "Estimated time left:"; static char xferText[] = "Transfer rate:"; int downloadSize, downloadCount, downloadTime; char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64]; int xferRate; int width, leftWidth; int style = UI_LEFT|UI_SMALLFONT|UI_DROPSHADOW; const char *s; downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" ); downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" ); downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" ); #if 0 // bk010104 fprintf( stderr, "\n\n-----------------------------------------------\n"); fprintf( stderr, "DB: downloadSize: %16d\n", downloadSize ); fprintf( stderr, "DB: downloadCount: %16d\n", downloadCount ); fprintf( stderr, "DB: downloadTime: %16d\n", downloadTime ); fprintf( stderr, "DB: UI realtime: %16d\n", uis.realtime ); // bk fprintf( stderr, "DB: UI frametime: %16d\n", uis.frametime ); // bk #endif leftWidth = width = UI_ProportionalStringWidth( dlText ) * UI_ProportionalSizeScale( style ); width = UI_ProportionalStringWidth( etaText ) * UI_ProportionalSizeScale( style ); if (width > leftWidth) leftWidth = width; width = UI_ProportionalStringWidth( xferText ) * UI_ProportionalSizeScale( style ); if (width > leftWidth) leftWidth = width; leftWidth += 16; UI_DrawProportionalString( 8, 128, dlText, style, color_white ); UI_DrawProportionalString( 8, 160, etaText, style, color_white ); UI_DrawProportionalString( 8, 224, xferText, style, color_white ); if (downloadSize > 0) { s = va( "%s (%d%%)", downloadName, (int)( (float)downloadCount * 100.0f / downloadSize ) ); } else { s = downloadName; } UI_DrawProportionalString( leftWidth, 128, s, style, color_white ); UI_ReadableSize( dlSizeBuf, sizeof dlSizeBuf, downloadCount ); UI_ReadableSize( totalSizeBuf, sizeof totalSizeBuf, downloadSize ); if (downloadCount < 4096 || !downloadTime) { UI_DrawProportionalString( leftWidth, 160, "estimating", style, color_white ); UI_DrawProportionalString( leftWidth, 192, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white ); } else { // bk010108 //float elapsedTime = (float)(uis.realtime - downloadTime); // current - start (msecs) //elapsedTime = elapsedTime * 0.001f; // in seconds //if ( elapsedTime <= 0.0f ) elapsedTime == 0.0f; if ( (uis.realtime - downloadTime) / 1000) { xferRate = downloadCount / ((uis.realtime - downloadTime) / 1000); //xferRate = (int)( ((float)downloadCount) / elapsedTime); } else { xferRate = 0; } //fprintf( stderr, "DB: elapsedTime: %16.8f\n", elapsedTime ); // bk //fprintf( stderr, "DB: xferRate: %16d\n", xferRate ); // bk UI_ReadableSize( xferRateBuf, sizeof xferRateBuf, xferRate ); // Extrapolate estimated completion time if (downloadSize && xferRate) { int n = downloadSize / xferRate; // estimated time for entire d/l in secs // We do it in K (/1024) because we'd overflow around 4MB n = (n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000; UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf, n ); // bk010104 //(n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000); UI_DrawProportionalString( leftWidth, 160, dlTimeBuf, style, color_white ); UI_DrawProportionalString( leftWidth, 192, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white ); } else { UI_DrawProportionalString( leftWidth, 160, "estimating", style, color_white ); if (downloadSize) { UI_DrawProportionalString( leftWidth, 192, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white ); } else { UI_DrawProportionalString( leftWidth, 192, va("(%s copied)", dlSizeBuf), style, color_white ); } } if (xferRate) { UI_DrawProportionalString( leftWidth, 224, va("%s/Sec", xferRateBuf), style, color_white ); } } } /* ======================== UI_DrawConnectScreen This will also be overlaid on the cgame info screen during loading to prevent it from blinking away too rapidly on local or lan games. ======================== */ void UI_DrawConnectScreen( qboolean overlay ) { char *s; uiClientState_t cstate; char info[MAX_INFO_VALUE]; Menu_Cache(); if ( !overlay ) { // draw the dialog background UI_SetColor( color_white ); UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader ); } // see what information we should display trap_GetClientState( &cstate ); info[0] = '\0'; if( trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ) ) { UI_DrawProportionalString( 320, 16, va( "Loading %s", Info_ValueForKey( info, "mapname" ) ), UI_BIGFONT|UI_CENTER|UI_DROPSHADOW, color_white ); } UI_DrawProportionalString( 320, 64, va("Connecting to %s", cstate.servername), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); //UI_DrawProportionalString( 320, 96, "Press Esc to abort", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); // display global MOTD at bottom UI_DrawProportionalString( SCREEN_WIDTH/2, SCREEN_HEIGHT-32, Info_ValueForKey( cstate.updateInfoString, "motd" ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); // print any server info (server full, bad version, etc) if ( cstate.connState < CA_CONNECTED ) { UI_DrawProportionalString_AutoWrapped( 320, 192, 630, 20, cstate.messageString, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); } #if 0 // display password field if ( passwordNeeded ) { s_ingame_menu.x = SCREEN_WIDTH * 0.50 - 128; s_ingame_menu.nitems = 0; s_ingame_menu.wrapAround = qtrue; passwordField.generic.type = MTYPE_FIELD; passwordField.generic.name = "Password:"; passwordField.generic.callback = 0; passwordField.generic.x = 10; passwordField.generic.y = 180; Field_Clear( &passwordField.field ); passwordField.width = 256; passwordField.field.widthInChars = 16; Q_strncpyz( passwordField.field.buffer, Cvar_VariableString("password"), sizeof(passwordField.field.buffer) ); Menu_AddItem( &s_ingame_menu, ( void * ) &s_customize_player_action ); MField_Draw( &passwordField ); } #endif if ( lastConnState > cstate.connState ) { lastLoadingText[0] = '\0'; } lastConnState = cstate.connState; switch ( cstate.connState ) { case CA_CONNECTING: s = va("Awaiting challenge...%i", cstate.connectPacketCount); break; case CA_CHALLENGING: s = va("Awaiting connection...%i", cstate.connectPacketCount); break; case CA_CONNECTED: { char downloadName[MAX_INFO_VALUE]; trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof(downloadName) ); if (*downloadName) { UI_DisplayDownloadInfo( downloadName ); return; } } s = "Awaiting gamestate..."; break; case CA_LOADING: return; case CA_PRIMED: return; default: return; } UI_DrawProportionalString( 320, 128, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, color_white ); // password required / connection rejected information goes here } /* =================== UI_KeyConnect =================== */ void UI_KeyConnect( int key ) { if ( key == K_ESCAPE ) { trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" ); return; } } openarena_0.8.8.orig/code/q3_ui/ui_teamorders.c0000644000175000017500000003321411656310261020166 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= TEAM ORDERS MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAME "menu/art_blueish/addbotframe" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ID_LIST_BOTS 10 #define ID_LIST_CTF_ORDERS 11 #define ID_LIST_CTF1F_ORDERS 12 #define ID_LIST_BASE_ORDERS 13 #define ID_LIST_TEAM_ORDERS 14 #define ID_LIST_DD_ORDERS 15 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s frame; menulist_s list; menubitmap_s back; int gametype; int numBots; int selectedBot; char *bots[9]; char botNames[9][16]; } teamOrdersMenuInfo_t; static teamOrdersMenuInfo_t teamOrdersMenuInfo; #define NUM_CTF_ORDERS 7 static const char *ctfOrders[] = { "I Am the Leader", "Defend the Base", "Follow Me", "Get Enemy Flag", "Camp Here", "Report", "I Relinquish Command", NULL }; static const char *ctfMessages[] = { "i am the leader", "%s defend the base", "%s follow me", "%s get the enemy flag", "%s camp here", "%s report", "i stop being the leader", NULL }; #define NUM_CTF1F_ORDERS 7 static const char *ctf1fOrders[] = { "I Am the Leader", "Defend the Base", "Follow Me", "Get The Flag", "Camp Here", "Report", "I Relinquish Command", NULL }; static const char *ctf1fMessages[] = { "i am the leader", "%s defend the base", "%s follow me", "%s get the flag", "%s camp here", "%s report", "i stop being the leader", NULL }; #define NUM_BASE_ORDERS 7 static const char *baseOrders[] = { "I Am the Leader", "Defend the Base", "Follow Me", "Attack the Enemy Base", "Camp Here", "Report", "I Relinquish Command", NULL }; static const char *baseMessages[] = { "i am the leader", "%s defend the base", "%s follow me", "%s attack the base", "%s camp here", "%s report", "i stop being the leader", NULL }; #define NUM_TEAM_ORDERS 6 static const char *teamOrders[] = { "I Am the Leader", "Follow Me", "Roam", "Camp Here", "Report", "I Relinquish Command", NULL }; static const char *teamMessages[] = { "i am the leader", "%s follow me", "%s roam", "%s camp here", "%s report", "i stop being the leader", NULL }; #define NUM_DD_ORDERS 8 static const char *ddOrders[] = { "I Am the Leader", "Follow Me", "Roam", "Dominate Point A", "Dominate Point B", "Camp Here", "Report", "I Relinquish Command", NULL }; static const char *ddMessages[] = { "i am the leader", "%s follow me", "%s roam", "%s dominate point A", "%s dominate point B", "%s camp here", "%s report", "i stop being the leader", NULL }; /* =============== UI_TeamOrdersMenu_BackEvent =============== */ static void UI_TeamOrdersMenu_BackEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); } /* =============== UI_TeamOrdersMenu_SetList =============== */ static void UI_TeamOrdersMenu_SetList( int id ) { switch( id ) { default: case ID_LIST_BOTS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = teamOrdersMenuInfo.numBots; teamOrdersMenuInfo.list.itemnames = (const char **)teamOrdersMenuInfo.bots; break; case ID_LIST_CTF_ORDERS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = NUM_CTF_ORDERS; teamOrdersMenuInfo.list.itemnames = ctfOrders; break; case ID_LIST_CTF1F_ORDERS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = NUM_CTF1F_ORDERS; teamOrdersMenuInfo.list.itemnames = ctf1fOrders; break; case ID_LIST_BASE_ORDERS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = NUM_BASE_ORDERS; teamOrdersMenuInfo.list.itemnames = baseOrders; break; case ID_LIST_TEAM_ORDERS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = NUM_TEAM_ORDERS; teamOrdersMenuInfo.list.itemnames = teamOrders; break; case ID_LIST_DD_ORDERS: teamOrdersMenuInfo.list.generic.id = id; teamOrdersMenuInfo.list.numitems = NUM_DD_ORDERS; teamOrdersMenuInfo.list.itemnames = ddOrders; break; } teamOrdersMenuInfo.list.generic.bottom = teamOrdersMenuInfo.list.generic.top + teamOrdersMenuInfo.list.numitems * PROP_HEIGHT; } /* ================= UI_TeamOrdersMenu_Key ================= */ sfxHandle_t UI_TeamOrdersMenu_Key( int key ) { menulist_s *l; int x; int y; int index; l = (menulist_s *)Menu_ItemAtCursor( &teamOrdersMenuInfo.menu ); if( l != &teamOrdersMenuInfo.list ) { return Menu_DefaultKey( &teamOrdersMenuInfo.menu, key ); } switch( key ) { case K_MOUSE1: x = l->generic.left; y = l->generic.top; if( UI_CursorInRect( x, y, l->generic.right - x, l->generic.bottom - y ) ) { index = (uis.cursory - y) / PROP_HEIGHT; l->oldvalue = l->curvalue; l->curvalue = index; if( l->generic.callback ) { l->generic.callback( l, QM_ACTIVATED ); return menu_move_sound; } } return menu_null_sound; case K_KP_UPARROW: case K_UPARROW: l->oldvalue = l->curvalue; if( l->curvalue == 0 ) { l->curvalue = l->numitems - 1; } else { l->curvalue--; } return menu_move_sound; case K_KP_DOWNARROW: case K_DOWNARROW: l->oldvalue = l->curvalue; if( l->curvalue == l->numitems - 1 ) { l->curvalue = 0;; } else { l->curvalue++; } return menu_move_sound; } return Menu_DefaultKey( &teamOrdersMenuInfo.menu, key ); } /* ================= UI_TeamOrdersMenu_ListDraw ================= */ static void UI_TeamOrdersMenu_ListDraw( void *self ) { menulist_s *l; int x; int y; int i; float *color; qboolean hasfocus; int style; l = (menulist_s *)self; hasfocus = (l->generic.parent->cursor == l->generic.menuPosition); x = 320;//l->generic.x; y = l->generic.y; for( i = 0; i < l->numitems; i++ ) { style = UI_LEFT|UI_SMALLFONT|UI_CENTER; if( i == l->curvalue ) { color = color_yellow; if( hasfocus ) { style |= UI_PULSE; } } else { color = color_orange; } UI_DrawProportionalString( x, y, l->itemnames[i], style, color ); y += PROP_HEIGHT; } } /* =============== UI_TeamOrdersMenu_ListEvent =============== */ static void UI_TeamOrdersMenu_ListEvent( void *ptr, int event ) { int id; int selection; char message[256]; if (event != QM_ACTIVATED) return; id = ((menulist_s *)ptr)->generic.id; selection = ((menulist_s *)ptr)->curvalue; if( id == ID_LIST_BOTS ) { teamOrdersMenuInfo.selectedBot = selection; if( teamOrdersMenuInfo.gametype == GT_CTF || teamOrdersMenuInfo.gametype == GT_CTF_ELIMINATION ) { UI_TeamOrdersMenu_SetList( ID_LIST_CTF_ORDERS ); } if( teamOrdersMenuInfo.gametype == GT_1FCTF ) { UI_TeamOrdersMenu_SetList( ID_LIST_CTF1F_ORDERS ); } if( teamOrdersMenuInfo.gametype == GT_OBELISK || teamOrdersMenuInfo.gametype == GT_HARVESTER ) { UI_TeamOrdersMenu_SetList( ID_LIST_BASE_ORDERS ); } if( teamOrdersMenuInfo.gametype == GT_TEAM || teamOrdersMenuInfo.gametype == GT_ELIMINATION || teamOrdersMenuInfo.gametype == GT_DOMINATION ) { UI_TeamOrdersMenu_SetList( ID_LIST_TEAM_ORDERS ); } if( teamOrdersMenuInfo.gametype == GT_DOUBLE_D ) { UI_TeamOrdersMenu_SetList( ID_LIST_DD_ORDERS ); } return; } if( id == ID_LIST_CTF_ORDERS ) { Com_sprintf( message, sizeof(message), ctfMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] ); } if( id == ID_LIST_CTF1F_ORDERS ) { Com_sprintf( message, sizeof(message), ctf1fMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] ); } if( id == ID_LIST_BASE_ORDERS ) { Com_sprintf( message, sizeof(message), baseMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] ); } if( id == ID_LIST_TEAM_ORDERS ) { Com_sprintf( message, sizeof(message), teamMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] ); } if( id == ID_LIST_DD_ORDERS ) { Com_sprintf( message, sizeof(message), ddMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] ); } trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"%s\"\n", message ) ); UI_PopMenu(); } /* =============== UI_TeamOrdersMenu_BuildBotList =============== */ static void UI_TeamOrdersMenu_BuildBotList( void ) { uiClientState_t cs; int numPlayers; int isBot; int n; char playerTeam = '3'; char botTeam; char info[MAX_INFO_STRING]; for( n = 0; n < 9; n++ ) { teamOrdersMenuInfo.bots[n] = teamOrdersMenuInfo.botNames[n]; } trap_GetClientState( &cs ); Q_strncpyz( teamOrdersMenuInfo.botNames[0], "Everyone", 16 ); teamOrdersMenuInfo.numBots = 1; trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); numPlayers = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); teamOrdersMenuInfo.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); for( n = 0; n < numPlayers && teamOrdersMenuInfo.numBots < 9; n++ ) { trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); if( n == cs.clientNum ) { playerTeam = *Info_ValueForKey( info, "t" ); continue; } isBot = atoi( Info_ValueForKey( info, "skill" ) ); if( !isBot ) { continue; } botTeam = *Info_ValueForKey( info, "t" ); if( botTeam != playerTeam ) { continue; } Q_strncpyz( teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.numBots], Info_ValueForKey( info, "n" ), 16 ); Q_CleanStr( teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.numBots] ); teamOrdersMenuInfo.numBots++; } } /* =============== UI_TeamOrdersMenu_Init =============== */ static void UI_TeamOrdersMenu_Init( void ) { UI_TeamOrdersMenu_Cache(); memset( &teamOrdersMenuInfo, 0, sizeof(teamOrdersMenuInfo) ); teamOrdersMenuInfo.menu.fullscreen = qfalse; teamOrdersMenuInfo.menu.key = UI_TeamOrdersMenu_Key; UI_TeamOrdersMenu_BuildBotList(); teamOrdersMenuInfo.banner.generic.type = MTYPE_BTEXT; teamOrdersMenuInfo.banner.generic.x = 320; teamOrdersMenuInfo.banner.generic.y = 16; teamOrdersMenuInfo.banner.string = "TEAM ORDERS"; teamOrdersMenuInfo.banner.color = color_white; teamOrdersMenuInfo.banner.style = UI_CENTER; teamOrdersMenuInfo.frame.generic.type = MTYPE_BITMAP; teamOrdersMenuInfo.frame.generic.flags = QMF_INACTIVE; teamOrdersMenuInfo.frame.generic.name = ART_FRAME; teamOrdersMenuInfo.frame.generic.x = 320-233; teamOrdersMenuInfo.frame.generic.y = 240-166; teamOrdersMenuInfo.frame.width = 466; teamOrdersMenuInfo.frame.height = 332; teamOrdersMenuInfo.list.generic.type = MTYPE_SCROLLLIST; teamOrdersMenuInfo.list.generic.flags = QMF_PULSEIFFOCUS; teamOrdersMenuInfo.list.generic.ownerdraw = UI_TeamOrdersMenu_ListDraw; teamOrdersMenuInfo.list.generic.callback = UI_TeamOrdersMenu_ListEvent; teamOrdersMenuInfo.list.generic.x = 320-64; teamOrdersMenuInfo.list.generic.y = 120; teamOrdersMenuInfo.back.generic.type = MTYPE_BITMAP; teamOrdersMenuInfo.back.generic.name = ART_BACK0; teamOrdersMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; teamOrdersMenuInfo.back.generic.callback = UI_TeamOrdersMenu_BackEvent; teamOrdersMenuInfo.back.generic.x = 0; teamOrdersMenuInfo.back.generic.y = 480-64; teamOrdersMenuInfo.back.width = 128; teamOrdersMenuInfo.back.height = 64; teamOrdersMenuInfo.back.focuspic = ART_BACK1; Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.banner ); Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.frame ); Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.list ); Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.back ); teamOrdersMenuInfo.list.generic.left = 220; teamOrdersMenuInfo.list.generic.top = teamOrdersMenuInfo.list.generic.y; teamOrdersMenuInfo.list.generic.right = 420; UI_TeamOrdersMenu_SetList( ID_LIST_BOTS ); } /* ================= UI_TeamOrdersMenu_Cache ================= */ void UI_TeamOrdersMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAME ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== UI_TeamOrdersMenu =============== */ void UI_TeamOrdersMenu( void ) { UI_TeamOrdersMenu_Init(); UI_PushMenu( &teamOrdersMenuInfo.menu ); } /* =============== UI_TeamOrdersMenu_f =============== */ void UI_TeamOrdersMenu_f( void ) { uiClientState_t cs; char info[MAX_INFO_STRING]; int team; // make sure it's a team game trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); teamOrdersMenuInfo.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); if( teamOrdersMenuInfo.gametype < GT_TEAM || teamOrdersMenuInfo.gametype!=GT_LMS) { return; } // not available to spectators trap_GetClientState( &cs ); trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING ); team = atoi( Info_ValueForKey( info, "t" ) ); if( team == TEAM_SPECTATOR ) { return; } UI_TeamOrdersMenu(); } openarena_0.8.8.orig/code/q3_ui/ui_playersettings.c0000644000175000017500000004132511656310261021100 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_MODEL0 "menu/art_blueish/model_0" #define ART_MODEL1 "menu/art_blueish/model_1" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FX_BASE "menu/art/fx_base" #define ART_FX_BLUE "menu/art/fx_blue" #define ART_FX_CYAN "menu/art/fx_cyan" #define ART_FX_GREEN "menu/art/fx_grn" #define ART_FX_RED "menu/art/fx_red" #define ART_FX_TEAL "menu/art/fx_teal" #define ART_FX_WHITE "menu/art/fx_white" #define ART_FX_YELLOW "menu/art/fx_yel" #define ID_NAME 10 #define ID_HANDICAP 11 #define ID_EFFECTS 12 #define ID_EFFECTS2 13 #define ID_BACK 14 #define ID_MODEL 15 #define MAX_NAMELENGTH 20 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menubitmap_s player; menufield_s name; menulist_s handicap; menulist_s effects; //Added in beta 29 menulist_s effects2; menubitmap_s back; menubitmap_s model; menubitmap_s item_null; qhandle_t fxBasePic; qhandle_t fxPic[7]; playerInfo_t playerinfo; int current_fx; char playerModel[MAX_QPATH]; } playersettings_t; static playersettings_t s_playersettings; static int gamecodetoui[] = {4,2,3,0,5,1,6}; static int uitogamecode[] = {4,6,2,3,1,5,7}; static const char *handicap_items[] = { "100", "95", "90", "85", "80", "75", "70", "65", "60", "55", "50", "45", "40", "35", "30", "25", "20", "15", "10", "5", NULL }; /* ================= PlayerSettings_DrawName ================= */ static void PlayerSettings_DrawName( void *self ) { menufield_s *f; qboolean focus; int style; char *txt; char c; float *color; int n; int basex, x, y; char name[32]; f = (menufield_s*)self; basex = f->generic.x; y = f->generic.y; focus = (f->generic.parent->cursor == f->generic.menuPosition); style = UI_LEFT|UI_SMALLFONT; color = text_color_normal; if( focus ) { style |= UI_PULSE; color = text_color_highlight; } UI_DrawProportionalString( basex, y, "Name", style, color ); // draw the actual name basex += 64; y += PROP_HEIGHT; txt = f->field.buffer; color = g_color_table[ColorIndex(COLOR_WHITE)]; x = basex; while ( (c = *txt) != 0 ) { if ( !focus && Q_IsColorString( txt ) ) { n = ColorIndex( *(txt+1) ); if( n == 0 ) { n = 7; } color = g_color_table[n]; txt += 2; continue; } UI_DrawChar( x, y, c, style, color ); txt++; x += SMALLCHAR_WIDTH; } // draw cursor if we have focus if( focus ) { if ( trap_Key_GetOverstrikeMode() ) { c = 11; } else { c = 10; } style &= ~UI_PULSE; style |= UI_BLINK; UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white ); } // draw at bottom also using proportional font Q_strncpyz( name, f->field.buffer, sizeof(name) ); Q_CleanStr( name ); UI_DrawProportionalString( 320, 440, name, UI_CENTER|UI_BIGFONT, text_color_normal ); } /* ================= PlayerSettings_DrawHandicap ================= */ static void PlayerSettings_DrawHandicap( void *self ) { menulist_s *item; qboolean focus; int style; float *color; item = (menulist_s *)self; focus = (item->generic.parent->cursor == item->generic.menuPosition); style = UI_LEFT|UI_SMALLFONT; color = text_color_normal; if( focus ) { style |= UI_PULSE; color = text_color_highlight; } UI_DrawProportionalString( item->generic.x, item->generic.y, "Handicap", style, color ); UI_DrawProportionalString( item->generic.x + 64, item->generic.y + PROP_HEIGHT, handicap_items[item->curvalue], style, color ); } /* ================= PlayerSettings_DrawEffects ================= */ static void PlayerSettings_DrawEffects( void *self ) { menulist_s *item; qboolean focus; int style; float *color; item = (menulist_s *)self; focus = (item->generic.parent->cursor == item->generic.menuPosition); style = UI_LEFT|UI_SMALLFONT; color = text_color_normal; if( focus ) { style |= UI_PULSE; color = text_color_highlight; } UI_DrawProportionalString( item->generic.x, item->generic.y, "Effects", style, color ); UI_DrawHandlePic( item->generic.x + 64, item->generic.y + PROP_HEIGHT + 8, 128, 8, s_playersettings.fxBasePic ); UI_DrawHandlePic( item->generic.x + 64 + item->curvalue * 16 + 8, item->generic.y + PROP_HEIGHT + 6, 16, 12, s_playersettings.fxPic[item->curvalue] ); } /* ================= PlayerSettings_DrawEffects ================= */ static void PlayerSettings_DrawEffects2( void *self ) { menulist_s *item; qboolean focus; item = (menulist_s *)self; focus = (item->generic.parent->cursor == item->generic.menuPosition); UI_DrawHandlePic( item->generic.x + 64, item->generic.y + 8, 128, 8, s_playersettings.fxBasePic ); UI_DrawHandlePic( item->generic.x + 64 + item->curvalue * 16 + 8, item->generic.y + 6, 16, 12, s_playersettings.fxPic[item->curvalue] ); } /* ================= PlayerSettings_DrawPlayer ================= */ static void PlayerSettings_DrawPlayer( void *self ) { menubitmap_s *b; vec3_t viewangles; char buf[MAX_QPATH]; trap_Cvar_VariableStringBuffer( "model", buf, sizeof( buf ) ); if ( strcmp( buf, s_playersettings.playerModel ) != 0 ) { UI_PlayerInfo_SetModel( &s_playersettings.playerinfo, buf ); strcpy( s_playersettings.playerModel, buf ); viewangles[YAW] = 180 - 30; viewangles[PITCH] = 0; viewangles[ROLL] = 0; UI_PlayerInfo_SetInfo( &s_playersettings.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); } b = (menubitmap_s*) self; UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_playersettings.playerinfo, uis.realtime/2 ); } /* ================= PlayerSettings_SaveChanges ================= */ static void PlayerSettings_SaveChanges( void ) { // name trap_Cvar_Set( "name", s_playersettings.name.field.buffer ); // handicap trap_Cvar_SetValue( "handicap", 100 - s_playersettings.handicap.curvalue * 5 ); // effects color trap_Cvar_SetValue( "color1", uitogamecode[s_playersettings.effects.curvalue] ); // effects2 color trap_Cvar_SetValue( "color2", uitogamecode[s_playersettings.effects2.curvalue] ); } /* ================= PlayerSettings_MenuKey ================= */ static sfxHandle_t PlayerSettings_MenuKey( int key ) { if( key == K_MOUSE2 || key == K_ESCAPE ) { PlayerSettings_SaveChanges(); } return Menu_DefaultKey( &s_playersettings.menu, key ); } /* ================= PlayerSettings_SetMenuItems ================= */ static void PlayerSettings_SetMenuItems( void ) { vec3_t viewangles; int c; int h; // name Q_strncpyz( s_playersettings.name.field.buffer, UI_Cvar_VariableString("name"), sizeof(s_playersettings.name.field.buffer) ); // effects color c = trap_Cvar_VariableValue( "color1" ) - 1; if( c < 0 || c > 6 ) { c = 6; } s_playersettings.effects.curvalue = gamecodetoui[c]; // effects2 color c = trap_Cvar_VariableValue( "color2" ) - 1; if( c < 0 || c > 6 ) { c = 6; } s_playersettings.effects2.curvalue = gamecodetoui[c]; // model/skin memset( &s_playersettings.playerinfo, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 30; viewangles[PITCH] = 0; viewangles[ROLL] = 0; UI_PlayerInfo_SetModel( &s_playersettings.playerinfo, UI_Cvar_VariableString( "model" ) ); UI_PlayerInfo_SetInfo( &s_playersettings.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); // handicap h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); s_playersettings.handicap.curvalue = 20 - h / 5; } /* ================= PlayerSettings_MenuEvent ================= */ static void PlayerSettings_MenuEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_HANDICAP: trap_Cvar_Set( "handicap", va( "%i", 100 - 25 * s_playersettings.handicap.curvalue ) ); break; case ID_MODEL: PlayerSettings_SaveChanges(); UI_PlayerModelMenu(); break; case ID_BACK: PlayerSettings_SaveChanges(); UI_PopMenu(); break; } } /* ================= PlayerSettings_StatusBar ================= */ static void PlayerSettings_StatusBar( void* ptr ) { UI_DrawString( 320, 400, "Lower handicap makes you weaker", UI_CENTER|UI_SMALLFONT, colorWhite ); UI_DrawString( 320, 420, "giving you more challenge", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= PlayerSettings_MenuInit ================= */ static void PlayerSettings_MenuInit( void ) { int y; memset(&s_playersettings,0,sizeof(playersettings_t)); PlayerSettings_Cache(); s_playersettings.menu.key = PlayerSettings_MenuKey; s_playersettings.menu.wrapAround = qtrue; s_playersettings.menu.fullscreen = qtrue; s_playersettings.banner.generic.type = MTYPE_BTEXT; s_playersettings.banner.generic.x = 320; s_playersettings.banner.generic.y = 16; s_playersettings.banner.string = "PLAYER SETTINGS"; s_playersettings.banner.color = color_white; s_playersettings.banner.style = UI_CENTER; s_playersettings.framel.generic.type = MTYPE_BITMAP; s_playersettings.framel.generic.name = ART_FRAMEL; s_playersettings.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_playersettings.framel.generic.x = 0; s_playersettings.framel.generic.y = 78; s_playersettings.framel.width = 256; s_playersettings.framel.height = 329; s_playersettings.framer.generic.type = MTYPE_BITMAP; s_playersettings.framer.generic.name = ART_FRAMER; s_playersettings.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_playersettings.framer.generic.x = 376; s_playersettings.framer.generic.y = 76; s_playersettings.framer.width = 256; s_playersettings.framer.height = 334; y = 144; s_playersettings.name.generic.type = MTYPE_FIELD; s_playersettings.name.generic.flags = QMF_NODEFAULTINIT; s_playersettings.name.generic.ownerdraw = PlayerSettings_DrawName; s_playersettings.name.field.widthInChars = MAX_NAMELENGTH; s_playersettings.name.field.maxchars = MAX_NAMELENGTH; s_playersettings.name.generic.x = 192; s_playersettings.name.generic.y = y; s_playersettings.name.generic.left = 192 - 8; s_playersettings.name.generic.top = y - 8; s_playersettings.name.generic.right = 192 + 200; s_playersettings.name.generic.bottom = y + 2 * PROP_HEIGHT; y += 3 * PROP_HEIGHT; s_playersettings.handicap.generic.type = MTYPE_SPINCONTROL; s_playersettings.handicap.generic.flags = QMF_NODEFAULTINIT; s_playersettings.handicap.generic.id = ID_HANDICAP; s_playersettings.handicap.generic.ownerdraw = PlayerSettings_DrawHandicap; s_playersettings.handicap.generic.x = 192; s_playersettings.handicap.generic.y = y; s_playersettings.handicap.generic.left = 192 - 8; s_playersettings.handicap.generic.top = y - 8; s_playersettings.handicap.generic.right = 192 + 200; s_playersettings.handicap.generic.bottom = y + 2 * PROP_HEIGHT; s_playersettings.handicap.generic.statusbar = PlayerSettings_StatusBar; s_playersettings.handicap.numitems = 20; y += 3 * PROP_HEIGHT; s_playersettings.effects.generic.type = MTYPE_SPINCONTROL; s_playersettings.effects.generic.flags = QMF_NODEFAULTINIT; s_playersettings.effects.generic.id = ID_EFFECTS; s_playersettings.effects.generic.ownerdraw = PlayerSettings_DrawEffects; s_playersettings.effects.generic.x = 192; s_playersettings.effects.generic.y = y; s_playersettings.effects.generic.left = 192 - 8; s_playersettings.effects.generic.top = y - 8; s_playersettings.effects.generic.right = 192 + 200; s_playersettings.effects.generic.bottom = y + 2* PROP_HEIGHT; s_playersettings.effects.numitems = 7; y += 2*PROP_HEIGHT; s_playersettings.effects2.generic.type = MTYPE_SPINCONTROL; s_playersettings.effects2.generic.flags = QMF_NODEFAULTINIT; s_playersettings.effects2.generic.id = ID_EFFECTS2; s_playersettings.effects2.generic.ownerdraw = PlayerSettings_DrawEffects2; s_playersettings.effects2.generic.x = 192; s_playersettings.effects2.generic.y = y; s_playersettings.effects2.generic.left = 192 - 8; s_playersettings.effects2.generic.top = y - 8; s_playersettings.effects2.generic.right = 192 + 200; s_playersettings.effects2.generic.bottom = y + 2* PROP_HEIGHT; s_playersettings.effects2.numitems = 7; s_playersettings.model.generic.type = MTYPE_BITMAP; s_playersettings.model.generic.name = ART_MODEL0; s_playersettings.model.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_playersettings.model.generic.id = ID_MODEL; s_playersettings.model.generic.callback = PlayerSettings_MenuEvent; s_playersettings.model.generic.x = 640; s_playersettings.model.generic.y = 480-64; s_playersettings.model.width = 128; s_playersettings.model.height = 64; s_playersettings.model.focuspic = ART_MODEL1; s_playersettings.player.generic.type = MTYPE_BITMAP; s_playersettings.player.generic.flags = QMF_INACTIVE; s_playersettings.player.generic.ownerdraw = PlayerSettings_DrawPlayer; s_playersettings.player.generic.x = 400; s_playersettings.player.generic.y = -40; s_playersettings.player.width = 32*10; s_playersettings.player.height = 56*10; s_playersettings.back.generic.type = MTYPE_BITMAP; s_playersettings.back.generic.name = ART_BACK0; s_playersettings.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_playersettings.back.generic.id = ID_BACK; s_playersettings.back.generic.callback = PlayerSettings_MenuEvent; s_playersettings.back.generic.x = 0; s_playersettings.back.generic.y = 480-64; s_playersettings.back.width = 128; s_playersettings.back.height = 64; s_playersettings.back.focuspic = ART_BACK1; s_playersettings.item_null.generic.type = MTYPE_BITMAP; s_playersettings.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT; s_playersettings.item_null.generic.x = 0; s_playersettings.item_null.generic.y = 0; s_playersettings.item_null.width = 640; s_playersettings.item_null.height = 480; Menu_AddItem( &s_playersettings.menu, &s_playersettings.banner ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.framel ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.framer ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.name ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.handicap ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.effects ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.effects2 ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.model ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.back ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.player ); Menu_AddItem( &s_playersettings.menu, &s_playersettings.item_null ); PlayerSettings_SetMenuItems(); } /* ================= PlayerSettings_Cache ================= */ void PlayerSettings_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_MODEL0 ); trap_R_RegisterShaderNoMip( ART_MODEL1 ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); s_playersettings.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); s_playersettings.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); s_playersettings.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); s_playersettings.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); s_playersettings.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); s_playersettings.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); s_playersettings.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); s_playersettings.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); } /* ================= UI_PlayerSettingsMenu ================= */ void UI_PlayerSettingsMenu( void ) { PlayerSettings_MenuInit(); UI_PushMenu( &s_playersettings.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_signup.c0000644000175000017500000002077411656310261017335 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // ui_signup.c // #include "ui_local.h" #define SIGNUP_FRAME "menu/art_blueish/cut_frame" #define ID_NAME 100 #define ID_NAME_BOX 101 #define ID_PASSWORD 102 #define ID_PASSWORD_BOX 103 #define ID_AGAIN 104 #define ID_AGAIN_BOX 105 #define ID_EMAIL 106 #define ID_EMAIL_BOX 107 #define ID_SIGNUP 108 #define ID_CANCEL 109 typedef struct { menuframework_s menu; menubitmap_s frame; menutext_s name; menufield_s name_box; menutext_s password; menufield_s password_box; menutext_s again; menufield_s again_box; menutext_s email; menufield_s email_box; menutext_s signup; menutext_s cancel; } signup_t; static signup_t s_signup; static menuframework_s s_signup_menu; static menuaction_s s_signup_signup; static menuaction_s s_signup_cancel; static vec4_t s_signup_color_prompt = {1.00, 0.43, 0.00, 1.00}; /* =============== Signup_MenuEvent =============== */ static void Signup_MenuEvent( void* ptr, int event ) { //char cmd[1024]; if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_SIGNUP: if( strcmp(s_signup.password_box.field.buffer, s_signup.again_box.field.buffer) != 0 ) { // GRANK_FIXME - password mismatch break; } // set name //trap_Cvar_Set( "name", s_signup.name_box.field.buffer ); /* trap_Cvar_Set( "rank_name", s_signup.name_box.field.buffer ); trap_Cvar_Set( "rank_pwd", s_signup.password_box.field.buffer ); */ // create account /* sprintf( cmd, "cmd rank_create \"%s\" \"%s\" \"%s\"\n", s_signup.name_box.field.buffer, s_signup.password_box.field.buffer, s_signup.email_box.field.buffer ); trap_Cmd_ExecuteText( EXEC_APPEND, cmd ); */ trap_CL_UI_RankUserCreate( s_signup.name_box.field.buffer, s_signup.password_box.field.buffer, s_signup.email_box.field.buffer ); UI_ForceMenuOff(); break; case ID_CANCEL: UI_PopMenu(); break; } } /* =============== Signup_MenuInit =============== */ void Signup_MenuInit( void ) { grank_status_t status; int y; memset( &s_signup, 0, sizeof(s_signup) ); Signup_Cache(); s_signup.menu.wrapAround = qtrue; s_signup.menu.fullscreen = qfalse; s_signup.frame.generic.type = MTYPE_BITMAP; s_signup.frame.generic.flags = QMF_INACTIVE; s_signup.frame.generic.name = SIGNUP_FRAME; s_signup.frame.generic.x = 142; //320-233; s_signup.frame.generic.y = 118; //240-166; s_signup.frame.width = 359; //466; s_signup.frame.height = 256; //332; y = 194; s_signup.name.generic.type = MTYPE_PTEXT; s_signup.name.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_signup.name.generic.id = ID_NAME; s_signup.name.generic.x = 310; s_signup.name.generic.y = y; s_signup.name.string = "NAME"; s_signup.name.style = UI_RIGHT|UI_SMALLFONT; s_signup.name.color = s_signup_color_prompt; s_signup.name_box.generic.type = MTYPE_FIELD; s_signup.name_box.generic.ownerdraw = Rankings_DrawName; s_signup.name_box.generic.name = ""; s_signup.name_box.generic.flags = 0; s_signup.name_box.generic.x = 330; s_signup.name_box.generic.y = y; s_signup.name_box.field.widthInChars = 16; s_signup.name_box.field.maxchars = 16; y += 20; s_signup.password.generic.type = MTYPE_PTEXT; s_signup.password.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_signup.password.generic.id = ID_PASSWORD; s_signup.password.generic.x = 310; s_signup.password.generic.y = y; s_signup.password.string = "PASSWORD"; s_signup.password.style = UI_RIGHT|UI_SMALLFONT; s_signup.password.color = s_signup_color_prompt; s_signup.password_box.generic.type = MTYPE_FIELD; s_signup.password_box.generic.ownerdraw = Rankings_DrawPassword; s_signup.password_box.generic.name = ""; s_signup.password_box.generic.flags = 0; s_signup.password_box.generic.x = 330; s_signup.password_box.generic.y = y; s_signup.password_box.field.widthInChars = 16; s_signup.password_box.field.maxchars = 16; y += 20; s_signup.again.generic.type = MTYPE_PTEXT; s_signup.again.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_signup.again.generic.id = ID_AGAIN; s_signup.again.generic.x = 310; s_signup.again.generic.y = y; s_signup.again.string = "(AGAIN)"; s_signup.again.style = UI_RIGHT|UI_SMALLFONT; s_signup.again.color = s_signup_color_prompt; s_signup.again_box.generic.type = MTYPE_FIELD; s_signup.again_box.generic.ownerdraw = Rankings_DrawPassword; s_signup.again_box.generic.name = ""; s_signup.again_box.generic.flags = 0; s_signup.again_box.generic.x = 330; s_signup.again_box.generic.y = y; s_signup.again_box.field.widthInChars = 16; s_signup.again_box.field.maxchars = 16; y += 20; s_signup.email.generic.type = MTYPE_PTEXT; s_signup.email.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_signup.email.generic.id = ID_EMAIL; s_signup.email.generic.x = 310; s_signup.email.generic.y = y; s_signup.email.string = "EMAIL"; s_signup.email.style = UI_RIGHT|UI_SMALLFONT; s_signup.email.color = s_signup_color_prompt; s_signup.email_box.generic.type = MTYPE_FIELD; s_signup.email_box.generic.ownerdraw = Rankings_DrawText; s_signup.email_box.generic.name = ""; s_signup.email_box.generic.flags = 0; s_signup.email_box.generic.x = 330; s_signup.email_box.generic.y = y; s_signup.email_box.field.widthInChars = 16; s_signup.email_box.field.maxchars = MAX_EDIT_LINE; y += 40; s_signup.signup.generic.type = MTYPE_PTEXT; s_signup.signup.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_signup.signup.generic.id = ID_SIGNUP; s_signup.signup.generic.callback = Signup_MenuEvent; s_signup.signup.generic.x = 310; s_signup.signup.generic.y = y; s_signup.signup.string = "SIGN UP"; s_signup.signup.style = UI_RIGHT|UI_SMALLFONT; s_signup.signup.color = colorRed; s_signup.cancel.generic.type = MTYPE_PTEXT; s_signup.cancel.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_signup.cancel.generic.id = ID_CANCEL; s_signup.cancel.generic.callback = Signup_MenuEvent; s_signup.cancel.generic.x = 330; s_signup.cancel.generic.y = y; s_signup.cancel.string = "CANCEL"; s_signup.cancel.style = UI_LEFT|UI_SMALLFONT; s_signup.cancel.color = colorRed; y += 20; status = (grank_status_t)trap_Cvar_VariableValue("client_status"); if( (status != QGR_STATUS_NEW) && (status != QGR_STATUS_SPECTATOR) ) { s_signup.name_box.generic.flags |= QMF_INACTIVE; s_signup.password_box.generic.flags |= QMF_INACTIVE; s_signup.again_box.generic.flags |= QMF_INACTIVE; s_signup.email_box.generic.flags |= QMF_INACTIVE; s_signup.signup.generic.flags |= QMF_INACTIVE; s_signup.signup.color = colorMdGrey; } Menu_AddItem( &s_signup.menu, (void*) &s_signup.frame ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.name ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.name_box ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.password ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.password_box ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.again ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.again_box ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.email ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.email_box ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.signup ); Menu_AddItem( &s_signup.menu, (void*) &s_signup.cancel ); } /* =============== Signup_Cache =============== */ void Signup_Cache( void ) { trap_R_RegisterShaderNoMip( SIGNUP_FRAME ); } /* =============== UI_SignupMenu =============== */ void UI_SignupMenu( void ) { Signup_MenuInit(); UI_PushMenu ( &s_signup.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu.c0000644000175000017500000004407611656310261017673 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define VOTEMENU_BACK0 "menu/art_blueish/back_0" #define VOTEMENU_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" static char* votemenu_artlist[] = { VOTEMENU_BACK0, VOTEMENU_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; #define ID_BACK 100 #define ID_GO 101 #define ID_NEXTMAP 102 #define ID_RESTART 103 #define ID_DOWARMUP 104 #define ID_MAP 105 #define ID_KICK 106 #define ID_FRAG 107 #define ID_TIME 108 #define ID_GAMETYPE 109 #define ID_CUSTOM 110 #define ID_SHUFFLE 111 //This sorta dependend on number of vote options #define VOTEMENU_MENU_VERTICAL_SPACING 22 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s go; //Allowed votes: qboolean map_restart; qboolean nextmap; qboolean g_doWarmup; qboolean g_doWarmupEnabled; qboolean clientkick; qboolean map; qboolean gametype; qboolean fraglimit; qboolean timelimit; qboolean custom; qboolean shuffle; //Buttons: menutext_s bMapRestart; menutext_s bNextmap; menutext_s bDoWarmup; menutext_s bKick; menutext_s bMap; menutext_s bGametype; menutext_s bTimelimit; menutext_s bFraglimit; menutext_s bShuffle; menutext_s bCustom; int selection; } votemenu_t; static votemenu_t s_votemenu; void UI_VoteMenuMenuInternal( void ); /* ================= VoteMenu_CheckVoteNames ================= */ static void VoteMenu_CheckVoteNames( void ) { int voteflags; voteflags = trap_Cvar_VariableValue("cg_voteflags"); s_votemenu.map_restart = voteflags&VF_map_restart; s_votemenu.nextmap = voteflags&VF_nextmap; s_votemenu.map = voteflags&VF_map; s_votemenu.gametype = voteflags&VF_g_gametype; //We never use "kick" in menues, always clientkick s_votemenu.clientkick = voteflags&VF_clientkick; s_votemenu.g_doWarmup = voteflags&VF_g_doWarmup; s_votemenu.timelimit = voteflags&VF_timelimit; s_votemenu.fraglimit = voteflags&VF_fraglimit; s_votemenu.custom = voteflags&VF_custom; s_votemenu.shuffle = voteflags&VF_shuffle; } /* ================= VoteMenu_Event ================= */ static void VoteMenu_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } switch(s_votemenu.selection) { case ID_NEXTMAP: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote nextmap" ); UI_PopMenu(); break; case ID_RESTART: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote map_restart" ); UI_PopMenu(); break; case ID_DOWARMUP: if(s_votemenu.g_doWarmupEnabled) trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_doWarmup 0" ); else trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_doWarmup 1" ); UI_PopMenu(); break; case ID_SHUFFLE: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote shuffle" ); UI_PopMenu(); break; case ID_FRAG: UI_VoteFraglimitMenu(); //Don't pop! It will do a double pop if successfull break; case ID_CUSTOM: UI_VoteCustomMenu(); //Don't pop! It will do a double pop if successfull break; case ID_TIME: UI_VoteTimelimitMenu(); //Don't pop! It will do a double pop if successfull break; case ID_GAMETYPE: UI_VoteGametypeMenu(); //Don't pop! It will do a double pop if successfull break; case ID_KICK: UI_VoteKickMenu(); //Don't pop! It will do a double pop if successfull break; case ID_MAP: UI_VoteMapMenu(); //Don't pop! It will do a double pop if successfull break; }; break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu.selection != ((menucommon_s*)ptr)->id) { s_votemenu.selection = ((menucommon_s*)ptr)->id; UI_VoteMenuMenuInternal(); } break; } } /* ================= VoteMenu_Cache ================= */ static void VoteMenu_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!votemenu_artlist[i]) break; trap_R_RegisterShaderNoMip(votemenu_artlist[i]); } //Check all names VoteMenu_CheckVoteNames(); } /* ================= UI_VoteMenu_Draw ================= */ static void UI_VoteMenu_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu.menu ); } /* ================= UI_VoteMenuMenuInternal *Used then forcing a redraw ================= */ void UI_VoteMenuMenuInternal( void ) { int y; VoteMenu_Cache(); s_votemenu.menu.wrapAround = qtrue; s_votemenu.menu.fullscreen = qfalse; s_votemenu.menu.draw = UI_VoteMenu_Draw; s_votemenu.banner.generic.type = MTYPE_BTEXT; s_votemenu.banner.generic.x = 320; s_votemenu.banner.generic.y = 16; s_votemenu.banner.string = "CALL VOTE"; s_votemenu.banner.color = color_white; s_votemenu.banner.style = UI_CENTER; y = 98; s_votemenu.bNextmap.generic.type = MTYPE_PTEXT; s_votemenu.bNextmap.color = color_red; s_votemenu.bNextmap.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.nextmap) s_votemenu.bNextmap.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_NEXTMAP) s_votemenu.bNextmap.color = color_orange; s_votemenu.bNextmap.generic.x = 320; s_votemenu.bNextmap.generic.y = y; s_votemenu.bNextmap.generic.id = ID_NEXTMAP; s_votemenu.bNextmap.generic.callback = VoteMenu_Event; s_votemenu.bNextmap.string = "Next map"; s_votemenu.bNextmap.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bMapRestart.generic.type = MTYPE_PTEXT; s_votemenu.bMapRestart.color = color_red; s_votemenu.bMapRestart.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.map_restart) s_votemenu.bMapRestart.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection==ID_RESTART) s_votemenu.bMapRestart.color = color_orange; s_votemenu.bMapRestart.generic.x = 320; s_votemenu.bMapRestart.generic.y = y; s_votemenu.bMapRestart.generic.id = ID_RESTART; s_votemenu.bMapRestart.generic.callback = VoteMenu_Event; s_votemenu.bMapRestart.string = "Restart match"; s_votemenu.bMapRestart.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bShuffle.generic.type = MTYPE_PTEXT; s_votemenu.bShuffle.color = color_red; s_votemenu.bShuffle.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.map_restart) s_votemenu.bShuffle.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection==ID_SHUFFLE) s_votemenu.bShuffle.color = color_orange; s_votemenu.bShuffle.generic.x = 320; s_votemenu.bShuffle.generic.y = y; s_votemenu.bShuffle.generic.id = ID_SHUFFLE; s_votemenu.bShuffle.generic.callback = VoteMenu_Event; s_votemenu.bShuffle.string = "Shuffle teams"; s_votemenu.bShuffle.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bMap.generic.type = MTYPE_PTEXT; s_votemenu.bMap.color = color_red; s_votemenu.bMap.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.map) s_votemenu.bMap.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_MAP) s_votemenu.bMap.color = color_orange; s_votemenu.bMap.generic.x = 320; s_votemenu.bMap.generic.y = y; s_votemenu.bMap.generic.id = ID_MAP; s_votemenu.bMap.generic.callback = VoteMenu_Event; s_votemenu.bMap.string = "Change map"; s_votemenu.bMap.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bGametype.generic.type = MTYPE_PTEXT; s_votemenu.bGametype.color = color_red; s_votemenu.bGametype.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.gametype) s_votemenu.bGametype.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_GAMETYPE) s_votemenu.bGametype.color = color_orange; s_votemenu.bGametype.generic.x = 320; s_votemenu.bGametype.generic.y = y; s_votemenu.bGametype.generic.id = ID_GAMETYPE; s_votemenu.bGametype.generic.callback = VoteMenu_Event; s_votemenu.bGametype.string = "Change gametype"; s_votemenu.bGametype.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bKick.generic.type = MTYPE_PTEXT; s_votemenu.bKick.color = color_red; s_votemenu.bKick.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.clientkick) s_votemenu.bKick.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_KICK) s_votemenu.bKick.color = color_orange; s_votemenu.bKick.generic.x = 320; s_votemenu.bKick.generic.y = y; s_votemenu.bKick.generic.id = ID_KICK; s_votemenu.bKick.generic.callback = VoteMenu_Event; s_votemenu.bKick.string = "Kick player"; s_votemenu.bKick.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bDoWarmup.generic.type = MTYPE_PTEXT; s_votemenu.bDoWarmup.color = color_red; s_votemenu.bDoWarmup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.g_doWarmup) s_votemenu.bDoWarmup.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_DOWARMUP) s_votemenu.bDoWarmup.color = color_orange; s_votemenu.bDoWarmup.generic.x = 320; s_votemenu.bDoWarmup.generic.y = y; s_votemenu.bDoWarmup.generic.id = ID_DOWARMUP; s_votemenu.bDoWarmup.generic.callback = VoteMenu_Event; if(s_votemenu.g_doWarmupEnabled) s_votemenu.bDoWarmup.string = "Disable warmup"; else s_votemenu.bDoWarmup.string = "Enable warmup"; s_votemenu.bDoWarmup.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bFraglimit.generic.type = MTYPE_PTEXT; s_votemenu.bFraglimit.color = color_red; s_votemenu.bFraglimit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.fraglimit) s_votemenu.bFraglimit.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_FRAG) s_votemenu.bFraglimit.color = color_orange; s_votemenu.bFraglimit.generic.x = 320; s_votemenu.bFraglimit.generic.y = y; s_votemenu.bFraglimit.generic.id = ID_FRAG; s_votemenu.bFraglimit.generic.callback = VoteMenu_Event; s_votemenu.bFraglimit.string = "Change fraglimit"; s_votemenu.bFraglimit.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bTimelimit.generic.type = MTYPE_PTEXT; s_votemenu.bTimelimit.color = color_red; s_votemenu.bTimelimit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.timelimit) s_votemenu.bTimelimit.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_TIME) s_votemenu.bTimelimit.color = color_orange; s_votemenu.bTimelimit.generic.x = 320; s_votemenu.bTimelimit.generic.y = y; s_votemenu.bTimelimit.generic.id = ID_TIME; s_votemenu.bTimelimit.generic.callback = VoteMenu_Event; s_votemenu.bTimelimit.string = "Change timelimit"; s_votemenu.bTimelimit.style = UI_CENTER|UI_SMALLFONT; y+=VOTEMENU_MENU_VERTICAL_SPACING; s_votemenu.bCustom.generic.type = MTYPE_PTEXT; s_votemenu.bCustom.color = color_red; s_votemenu.bCustom.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!s_votemenu.custom) s_votemenu.bCustom.generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu.selection == ID_CUSTOM) s_votemenu.bCustom.color = color_orange; s_votemenu.bCustom.generic.x = 320; s_votemenu.bCustom.generic.y = y; s_votemenu.bCustom.generic.id = ID_CUSTOM; s_votemenu.bCustom.generic.callback = VoteMenu_Event; s_votemenu.bCustom.string = "Custom vote"; s_votemenu.bCustom.style = UI_CENTER|UI_SMALLFONT; s_votemenu.back.generic.type = MTYPE_BITMAP; s_votemenu.back.generic.name = VOTEMENU_BACK0; s_votemenu.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu.back.generic.callback = VoteMenu_Event; s_votemenu.back.generic.id = ID_BACK; s_votemenu.back.generic.x = 320-128; s_votemenu.back.generic.y = 256+128-64; s_votemenu.back.width = 128; s_votemenu.back.height = 64; s_votemenu.back.focuspic = VOTEMENU_BACK1; s_votemenu.go.generic.type = MTYPE_BITMAP; s_votemenu.go.generic.name = ART_FIGHT0; s_votemenu.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu.go.generic.callback = VoteMenu_Event; s_votemenu.go.generic.id = ID_GO; s_votemenu.go.generic.x = 320; s_votemenu.go.generic.y = 256+128-64; s_votemenu.go.width = 128; s_votemenu.go.height = 64; s_votemenu.go.focuspic = ART_FIGHT1; } /* ================= UI_VoteMenuMenu *Called from outside ================= */ void UI_VoteMenuMenu( void ) { // zero set all our globals char serverinfo[MAX_INFO_STRING]; memset( &s_votemenu, 0 ,sizeof(votemenu_t) ); trap_GetConfigString( CS_SERVERINFO, serverinfo, MAX_INFO_STRING ); s_votemenu.g_doWarmupEnabled = atoi(Info_ValueForKey(serverinfo,"g_doWarmup")); UI_VoteMenuMenuInternal(); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.banner ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.back ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.go ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bNextmap ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bMapRestart ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bShuffle ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bMap ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bGametype ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bKick ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bDoWarmup ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bFraglimit ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bTimelimit ); Menu_AddItem( &s_votemenu.menu, (void*) &s_votemenu.bCustom ); UI_PushMenu( &s_votemenu.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_map.c0000644000175000017500000002365111656310261020524 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" #define ART_ARROWS "menu/art_blueish/arrows_vert_0" #define ART_ARROWUP "menu/art_blueish/arrows_vert_top" #define ART_ARROWDOWN "menu/art_blueish/arrows_vert_bot" #define ID_BACK 10 #define ID_GO 11 #define ID_LIST 12 #define ID_UP 13 #define ID_DOWN 14 #define ID_MAPNAME0 20 #define ID_MAPNAME1 21 #define ID_MAPNAME2 22 #define ID_MAPNAME3 23 #define ID_MAPNAME4 24 #define ID_MAPNAME5 25 #define ID_MAPNAME6 26 #define ID_MAPNAME7 27 #define ID_MAPNAME8 28 #define ID_MAPNAME9 29 #define SIZE_OF_LIST 10 #define SIZE_OF_NAME 32 #define VOTEMAP_MENU_VERTICAL_SPACING 20 typedef struct { menuframework_s menu; menubitmap_s arrows; menutext_s banner; menutext_s info; menubitmap_s up; menubitmap_s down; menubitmap_s go; menubitmap_s back; menutext_s maps[SIZE_OF_LIST]; int selected; } votemenu_map_t; static votemenu_map_t s_votemenu_map; /* ================= VoteMapMenu_Event ================= */ static void VoteMapMenu_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) return; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } if(!s_votemenu_map.selected || mappage.mapname[s_votemenu_map.selected-ID_MAPNAME0][0] == 0) return; if(!Q_stricmp(mappage.mapname[s_votemenu_map.selected-ID_MAPNAME0],"---")) return; //Blank spaces have string "---" trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote map %s", mappage.mapname[s_votemenu_map.selected-ID_MAPNAME0]) ); UI_PopMenu(); UI_PopMenu(); break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_map.selected != ((menucommon_s*)ptr)->id) { s_votemenu_map.selected = ((menucommon_s*)ptr)->id; UI_VoteMapMenuInternal(); } break; } } /* ================= UI_VoteMapMenu_UpEvent ================= */ static void UI_VoteMapMenu_UpEvent( void* ptr, int event ) { if (event != QM_ACTIVATED || mappage.pagenumber<1) { return; } trap_Cmd_ExecuteText( EXEC_APPEND,va("getmappage %d",mappage.pagenumber-1) ); } /* ================= UI_VoteMapMenu_DownEvent ================= */ static void UI_VoteMapMenu_DownEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } trap_Cmd_ExecuteText( EXEC_APPEND,va("getmappage %d",mappage.pagenumber+1) ); } /* ================= UI_VoteMapMenu_Draw ================= */ static void UI_VoteMapMenu_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE MAP", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_map.menu ); } /* ================= VoteMapMenu_Cache ================= */ static void VoteMapMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_BACKGROUND ); trap_R_RegisterShaderNoMip( ART_ARROWS ); trap_R_RegisterShaderNoMip( ART_ARROWUP ); trap_R_RegisterShaderNoMip( ART_ARROWDOWN ); } static void setMapMenutext(menutext_s *menu,int y,int id,char * text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; if(s_votemenu_map.selected == id) menu->color = color_orange; menu->generic.x = 320-80; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteMapMenu_Event; menu->string = text; menu->style = UI_LEFT|UI_SMALLFONT; } /* ================= UI_VoteMapMenuInternal *Used then forcing a redraw ================= */ void UI_VoteMapMenuInternal( void ) { int y,i; s_votemenu_map.menu.wrapAround = qtrue; s_votemenu_map.menu.fullscreen = qfalse; s_votemenu_map.menu.draw = UI_VoteMapMenu_Draw; s_votemenu_map.banner.generic.type = MTYPE_BTEXT; s_votemenu_map.banner.generic.x = 320; s_votemenu_map.banner.generic.y = 16; s_votemenu_map.banner.string = "CALL VOTE MAP"; s_votemenu_map.banner.color = color_white; s_votemenu_map.banner.style = UI_CENTER; s_votemenu_map.info.generic.type = MTYPE_TEXT; s_votemenu_map.info.generic.x = 320; s_votemenu_map.info.generic.y = 48; s_votemenu_map.info.string = va("Page %d",mappage.pagenumber+1); s_votemenu_map.info.color = color_white; s_votemenu_map.info.style = UI_CENTER; s_votemenu_map.arrows.generic.type = MTYPE_BITMAP; s_votemenu_map.arrows.generic.name = ART_ARROWS; s_votemenu_map.arrows.generic.flags = QMF_INACTIVE; s_votemenu_map.arrows.generic.x = 200-40; s_votemenu_map.arrows.generic.y = 128+30; s_votemenu_map.arrows.width = 64; s_votemenu_map.arrows.height = 128; y = 98; setMapMenutext(&s_votemenu_map.maps[0],y,ID_MAPNAME0,mappage.mapname[0]); for(i=1;ioldFrame was exactly on int frame; int frameTime; // time when ->frame will be exactly on float backlerp; float yawAngle; qboolean yawing; float pitchAngle; qboolean pitching; int animationNumber; // may include ANIM_TOGGLEBIT animation_t *animation; int animationTime; // time when the first frame of the animation will be exact } lerpFrame_t; typedef struct { // model info qhandle_t legsModel; qhandle_t legsSkin; lerpFrame_t legs; qhandle_t torsoModel; qhandle_t torsoSkin; lerpFrame_t torso; qhandle_t headModel; qhandle_t headSkin; animation_t animations[MAX_ANIMATIONS]; qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; vec3_t flashDlightColor; int muzzleFlashTime; // currently in use drawing parms vec3_t viewAngles; vec3_t moveAngles; weapon_t currentWeapon; int legsAnim; int torsoAnim; // animation vars weapon_t weapon; weapon_t lastWeapon; weapon_t pendingWeapon; int weaponTimer; int pendingLegsAnim; int torsoAnimationTimer; int pendingTorsoAnim; int legsAnimationTimer; qboolean chat; qboolean newModel; qboolean barrelSpinning; float barrelAngle; int barrelTime; int realWeapon; } playerInfo_t; void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ); void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ); void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNum, qboolean chat ); qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ); // // ui_atoms.c // typedef struct { int frametime; int realtime; int cursorx; int cursory; int menusp; menuframework_s* activemenu; menuframework_s* stack[MAX_MENUDEPTH]; glconfig_t glconfig; qboolean debug; qhandle_t whiteShader; qhandle_t menuBackShader; qhandle_t menuBackNoLogoShader; qhandle_t charset; qhandle_t charsetProp; qhandle_t charsetPropGlow; qhandle_t charsetPropB; qhandle_t cursor; qhandle_t rb_on; qhandle_t rb_off; float xscale; float yscale; float bias; qboolean demoversion; qboolean firstdraw; } uiStatic_t; extern void UI_Init( void ); extern void UI_Shutdown( void ); extern void UI_KeyEvent( int key, int down ); extern void UI_MouseEvent( int dx, int dy ); extern void UI_Refresh( int realtime ); extern qboolean UI_ConsoleCommand( int realTime ); extern float UI_ClampCvar( float min, float max, float value ); extern void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ); extern void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ); extern void UI_FillRect( float x, float y, float width, float height, const float *color ); extern void UI_DrawRect( float x, float y, float width, float height, const float *color ); extern void UI_UpdateScreen( void ); extern void UI_SetColor( const float *rgba ); extern void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t); extern void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ); extern float UI_ProportionalSizeScale( int style ); extern void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); extern void UI_DrawProportionalString_AutoWrapped( int x, int ystart, int xmax, int ystep, const char* str, int style, vec4_t color ); extern int UI_ProportionalStringWidth( const char* str ); extern void UI_DrawString( int x, int y, const char* str, int style, vec4_t color ); extern void UI_DrawChar( int x, int y, int ch, int style, vec4_t color ); extern qboolean UI_CursorInRect (int x, int y, int width, int height); extern void UI_AdjustFrom640( float *x, float *y, float *w, float *h ); extern void UI_DrawTextBox (int x, int y, int width, int lines); extern qboolean UI_IsFullscreen( void ); extern void UI_SetActiveMenu( uiMenuCommand_t menu ); extern void UI_PushMenu ( menuframework_s *menu ); extern void UI_PopMenu (void); extern void UI_ForceMenuOff (void); extern char *UI_Argv( int arg ); extern char *UI_Cvar_VariableString( const char *var_name ); extern void UI_Refresh( int time ); extern void UI_StartDemoLoop( void ); extern qboolean m_entersound; extern uiStatic_t uis; // // ui_spLevel.c // void UI_SPLevelMenu_Cache( void ); void UI_SPLevelMenu( void ); void UI_SPLevelMenu_f( void ); void UI_SPLevelMenu_ReInit( void ); // // ui_spArena.c // void UI_SPArena_Start( const char *arenaInfo ); // // ui_spPostgame.c // void UI_SPPostgameMenu_Cache( void ); void UI_SPPostgameMenu_f( void ); // // ui_spSkill.c // void UI_SPSkillMenu( const char *arenaInfo ); void UI_SPSkillMenu_Cache( void ); // // ui_syscalls.c // void trap_Print( const char *string ); void trap_Error( const char *string ) __attribute__((noreturn)); int trap_Milliseconds( void ); void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); void trap_Cvar_Update( vmCvar_t *vmCvar ); void trap_Cvar_Set( const char *var_name, const char *value ); float trap_Cvar_VariableValue( const char *var_name ); void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void trap_Cvar_SetValue( const char *var_name, float value ); void trap_Cvar_Reset( const char *name ); void trap_Cvar_Create( const char *var_name, const char *var_value, int flags ); void trap_Cvar_InfoStringBuffer( int bit, char *buffer, int bufsize ); int trap_Argc( void ); void trap_Argv( int n, char *buffer, int bufferLength ); void trap_Cmd_ExecuteText( int exec_when, const char *text ); // don't use EXEC_NOW! int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); void trap_FS_Read( void *buffer, int len, fileHandle_t f ); void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); void trap_FS_FCloseFile( fileHandle_t f ); int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t qhandle_t trap_R_RegisterModel( const char *name ); qhandle_t trap_R_RegisterSkin( const char *name ); qhandle_t trap_R_RegisterShaderNoMip( const char *name ); void trap_R_ClearScene( void ); void trap_R_AddRefEntityToScene( const refEntity_t *re ); void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ); void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); void trap_R_RenderScene( const refdef_t *fd ); void trap_R_SetColor( const float *rgba ); void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_UpdateScreen( void ); int trap_CM_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ); void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ); void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ); void trap_Key_SetBinding( int keynum, const char *binding ); qboolean trap_Key_IsDown( int keynum ); qboolean trap_Key_GetOverstrikeMode( void ); void trap_Key_SetOverstrikeMode( qboolean state ); void trap_Key_ClearStates( void ); int trap_Key_GetCatcher( void ); void trap_Key_SetCatcher( int catcher ); void trap_GetClipboardData( char *buf, int bufsize ); void trap_GetClientState( uiClientState_t *state ); void trap_GetGlconfig( glconfig_t *glconfig ); int trap_GetConfigString( int index, char* buff, int buffsize ); int trap_LAN_GetServerCount( int source ); void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen ); void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen ); int trap_LAN_GetPingQueueCount( void ); int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen ); void trap_LAN_ClearPing( int n ); void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime ); void trap_LAN_GetPingInfo( int n, char *buf, int buflen ); int trap_MemoryRemaining( void ); void trap_GetCDKey( char *buf, int buflen ); void trap_SetCDKey( char *buf ); qboolean trap_VerifyCDKey( const char *key, const char *chksum); // bk001208 - RC4 void trap_SetPbClStatus( int status ); // // ui_addbots.c // void UI_AddBots_Cache( void ); void UI_AddBotsMenu( void ); // // ui_removebots.c // void UI_RemoveBots_Cache( void ); void UI_RemoveBotsMenu( void ); // // ui_teamorders.c // extern void UI_TeamOrdersMenu( void ); extern void UI_TeamOrdersMenu_f( void ); extern void UI_TeamOrdersMenu_Cache( void ); // // ui_loadconfig.c // void UI_LoadConfig_Cache( void ); void UI_LoadConfigMenu( void ); // // ui_saveconfig.c // void UI_SaveConfigMenu_Cache( void ); void UI_SaveConfigMenu( void ); // // ui_display.c // void UI_DisplayOptionsMenu_Cache( void ); void UI_DisplayOptionsMenu( void ); // // ui_sound.c // void UI_SoundOptionsMenu_Cache( void ); void UI_SoundOptionsMenu( void ); // // ui_network.c // void UI_NetworkOptionsMenu_Cache( void ); void UI_NetworkOptionsMenu( void ); // // ui_gameinfo.c // typedef enum { AWARD_ACCURACY, AWARD_IMPRESSIVE, AWARD_EXCELLENT, AWARD_GAUNTLET, AWARD_FRAGS, AWARD_PERFECT } awardType_t; const char *UI_GetArenaInfoByNumber( int num ); const char *UI_GetArenaInfoByMap( const char *map ); const char *UI_GetSpecialArenaInfo( const char *tag ); int UI_GetNumArenas( void ); int UI_GetNumSPArenas( void ); int UI_GetNumSPTiers( void ); char *UI_GetBotInfoByNumber( int num ); char *UI_GetBotInfoByName( const char *name ); int UI_GetNumBots( void ); void UI_GetBestScore( int level, int *score, int *skill ); void UI_SetBestScore( int level, int score ); int UI_TierCompleted( int levelWon ); qboolean UI_ShowTierVideo( int tier ); qboolean UI_CanShowTierVideo( int tier ); int UI_GetCurrentGame( void ); void UI_NewGame( void ); void UI_LogAwardData( int award, int data ); int UI_GetAwardLevel( int award ); void UI_SPUnlock_f( void ); void UI_SPUnlockMedals_f( void ); void UI_InitGameinfo( void ); //GRank // // ui_rankings.c // void Rankings_DrawText( void* self ); void Rankings_DrawName( void* self ); void Rankings_DrawPassword( void* self ); void Rankings_Cache( void ); void UI_RankingsMenu( void ); // // ui_login.c // void Login_Cache( void ); void UI_LoginMenu( void ); // // ui_signup.c // void Signup_Cache( void ); void UI_SignupMenu( void ); // // ui_rankstatus.c // void RankStatus_Cache( void ); void UI_RankStatusMenu( void ); #endif openarena_0.8.8.orig/code/q3_ui/ui_display.c0000644000175000017500000002225011656310261017464 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= DISPLAY OPTIONS MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ID_GRAPHICS 10 #define ID_DISPLAY 11 #define ID_SOUND 12 #define ID_NETWORK 13 #define ID_BRIGHTNESS 14 #define ID_SCREENSIZE 15 #define ID_BACK 16 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s graphics; menutext_s display; menutext_s sound; menutext_s network; menuslider_s brightness; menuslider_s screensize; menubitmap_s back; } displayOptionsInfo_t; static displayOptionsInfo_t displayOptionsInfo; /* ================= UI_DisplayOptionsMenu_Event ================= */ static void UI_DisplayOptionsMenu_Event( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GRAPHICS: UI_PopMenu(); UI_GraphicsOptionsMenu(); break; case ID_DISPLAY: break; case ID_SOUND: UI_PopMenu(); UI_SoundOptionsMenu(); break; case ID_NETWORK: UI_PopMenu(); UI_NetworkOptionsMenu(); break; case ID_BRIGHTNESS: trap_Cvar_SetValue( "r_gamma", displayOptionsInfo.brightness.curvalue / 10.0f ); break; case ID_SCREENSIZE: trap_Cvar_SetValue( "cg_viewsize", displayOptionsInfo.screensize.curvalue * 10 ); break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_DisplayOptionsMenu_Init =============== */ static void UI_DisplayOptionsMenu_Init( void ) { int y; memset( &displayOptionsInfo, 0, sizeof(displayOptionsInfo) ); UI_DisplayOptionsMenu_Cache(); displayOptionsInfo.menu.wrapAround = qtrue; displayOptionsInfo.menu.fullscreen = qtrue; displayOptionsInfo.banner.generic.type = MTYPE_BTEXT; displayOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY; displayOptionsInfo.banner.generic.x = 320; displayOptionsInfo.banner.generic.y = 16; displayOptionsInfo.banner.string = "SYSTEM SETUP"; displayOptionsInfo.banner.color = color_white; displayOptionsInfo.banner.style = UI_CENTER; displayOptionsInfo.framel.generic.type = MTYPE_BITMAP; displayOptionsInfo.framel.generic.name = ART_FRAMEL; displayOptionsInfo.framel.generic.flags = QMF_INACTIVE; displayOptionsInfo.framel.generic.x = 0; displayOptionsInfo.framel.generic.y = 78; displayOptionsInfo.framel.width = 256; displayOptionsInfo.framel.height = 329; displayOptionsInfo.framer.generic.type = MTYPE_BITMAP; displayOptionsInfo.framer.generic.name = ART_FRAMER; displayOptionsInfo.framer.generic.flags = QMF_INACTIVE; displayOptionsInfo.framer.generic.x = 376; displayOptionsInfo.framer.generic.y = 76; displayOptionsInfo.framer.width = 256; displayOptionsInfo.framer.height = 334; displayOptionsInfo.graphics.generic.type = MTYPE_PTEXT; displayOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; displayOptionsInfo.graphics.generic.id = ID_GRAPHICS; displayOptionsInfo.graphics.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.graphics.generic.x = 216; displayOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT; displayOptionsInfo.graphics.string = "GRAPHICS"; displayOptionsInfo.graphics.style = UI_RIGHT; displayOptionsInfo.graphics.color = color_red; displayOptionsInfo.display.generic.type = MTYPE_PTEXT; displayOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY; displayOptionsInfo.display.generic.id = ID_DISPLAY; displayOptionsInfo.display.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.display.generic.x = 216; displayOptionsInfo.display.generic.y = 240 - PROP_HEIGHT; displayOptionsInfo.display.string = "DISPLAY"; displayOptionsInfo.display.style = UI_RIGHT; displayOptionsInfo.display.color = color_red; displayOptionsInfo.sound.generic.type = MTYPE_PTEXT; displayOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; displayOptionsInfo.sound.generic.id = ID_SOUND; displayOptionsInfo.sound.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.sound.generic.x = 216; displayOptionsInfo.sound.generic.y = 240; displayOptionsInfo.sound.string = "SOUND"; displayOptionsInfo.sound.style = UI_RIGHT; displayOptionsInfo.sound.color = color_red; displayOptionsInfo.network.generic.type = MTYPE_PTEXT; displayOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; displayOptionsInfo.network.generic.id = ID_NETWORK; displayOptionsInfo.network.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.network.generic.x = 216; displayOptionsInfo.network.generic.y = 240 + PROP_HEIGHT; displayOptionsInfo.network.string = "NETWORK"; displayOptionsInfo.network.style = UI_RIGHT; displayOptionsInfo.network.color = color_red; y = 240 - 1 * (BIGCHAR_HEIGHT+2); displayOptionsInfo.brightness.generic.type = MTYPE_SLIDER; displayOptionsInfo.brightness.generic.name = "Brightness:"; displayOptionsInfo.brightness.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; displayOptionsInfo.brightness.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.brightness.generic.id = ID_BRIGHTNESS; displayOptionsInfo.brightness.generic.x = 400; displayOptionsInfo.brightness.generic.y = y; displayOptionsInfo.brightness.minvalue = 5; displayOptionsInfo.brightness.maxvalue = 20; if( !uis.glconfig.deviceSupportsGamma ) { displayOptionsInfo.brightness.generic.flags |= QMF_GRAYED; } y += BIGCHAR_HEIGHT+2; displayOptionsInfo.screensize.generic.type = MTYPE_SLIDER; displayOptionsInfo.screensize.generic.name = "Screen Size:"; displayOptionsInfo.screensize.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; displayOptionsInfo.screensize.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.screensize.generic.id = ID_SCREENSIZE; displayOptionsInfo.screensize.generic.x = 400; displayOptionsInfo.screensize.generic.y = y; displayOptionsInfo.screensize.minvalue = 3; displayOptionsInfo.screensize.maxvalue = 10; displayOptionsInfo.back.generic.type = MTYPE_BITMAP; displayOptionsInfo.back.generic.name = ART_BACK0; displayOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; displayOptionsInfo.back.generic.callback = UI_DisplayOptionsMenu_Event; displayOptionsInfo.back.generic.id = ID_BACK; displayOptionsInfo.back.generic.x = 0; displayOptionsInfo.back.generic.y = 480-64; displayOptionsInfo.back.width = 128; displayOptionsInfo.back.height = 64; displayOptionsInfo.back.focuspic = ART_BACK1; Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.banner ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.framel ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.framer ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.graphics ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.display ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.sound ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.network ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.brightness ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.screensize ); Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.back ); displayOptionsInfo.brightness.curvalue = trap_Cvar_VariableValue("r_gamma") * 10; displayOptionsInfo.screensize.curvalue = trap_Cvar_VariableValue( "cg_viewsize")/10; } /* =============== UI_DisplayOptionsMenu_Cache =============== */ void UI_DisplayOptionsMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== UI_DisplayOptionsMenu =============== */ void UI_DisplayOptionsMenu( void ) { UI_DisplayOptionsMenu_Init(); UI_PushMenu( &displayOptionsInfo.menu ); Menu_SetCursorToItem( &displayOptionsInfo.menu, &displayOptionsInfo.display ); } openarena_0.8.8.orig/code/q3_ui/ui_password.c0000644000175000017500000001550111656310261017662 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /********************************************************************************* SPECIFY PASSWORD *********************************************************************************/ #define SPECIFYPASSWORD_FRAMEL "menu/art_blueish/frame2_l" #define SPECIFYPASSWORD_FRAMER "menu/art_blueish/frame1_r" #define SPECIFYPASSWORD_BACK0 "menu/art_blueish/back_0" #define SPECIFYPASSWORD_BACK1 "menu/art_blueish/back_1" #define SPECIFYPASSWORD_FIGHT0 "menu/art_blueish/fight_0" #define SPECIFYPASSWORD_FIGHT1 "menu/art_blueish/fight_1" #define ID_SPECIFYPASSWORDBACK 102 #define ID_SPECIFYPASSWORDGO 103 static char* specifypassword_artlist[] = { SPECIFYPASSWORD_FRAMEL, SPECIFYPASSWORD_FRAMER, SPECIFYPASSWORD_BACK0, SPECIFYPASSWORD_BACK1, SPECIFYPASSWORD_FIGHT0, SPECIFYPASSWORD_FIGHT1, NULL }; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s info; menufield_s password; menubitmap_s go; menubitmap_s back; char *connectstring; char servername[32]; } specifypassword_t; static specifypassword_t s_specifypassword; /* ================= SpecifyPassword_Event ================= */ static void SpecifyPassword_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_SPECIFYPASSWORDGO: if (event != QM_ACTIVATED) break; if (s_specifypassword.password.field.buffer[0]) { trap_Cvar_Set("password",s_specifypassword.password.field.buffer); trap_Cmd_ExecuteText( EXEC_APPEND, s_specifypassword.connectstring ); } break; case ID_SPECIFYPASSWORDBACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; } } /* ================= SpecifyPassword_MenuInit ================= */ void SpecifyPassword_MenuInit( void ) { // zero set all our globals memset( &s_specifypassword, 0 ,sizeof(specifypassword_t) ); SpecifyPassword_Cache(); s_specifypassword.menu.wrapAround = qtrue; s_specifypassword.menu.fullscreen = qtrue; s_specifypassword.banner.generic.type = MTYPE_BTEXT; s_specifypassword.banner.generic.x = 320; s_specifypassword.banner.generic.y = 16; s_specifypassword.banner.string = "SPECIFY PASSWORD"; s_specifypassword.banner.color = color_white; s_specifypassword.banner.style = UI_CENTER; s_specifypassword.framel.generic.type = MTYPE_BITMAP; s_specifypassword.framel.generic.name = SPECIFYPASSWORD_FRAMEL; s_specifypassword.framel.generic.flags = QMF_INACTIVE; s_specifypassword.framel.generic.x = 0; s_specifypassword.framel.generic.y = 78; s_specifypassword.framel.width = 256; s_specifypassword.framel.height = 329; s_specifypassword.framer.generic.type = MTYPE_BITMAP; s_specifypassword.framer.generic.name = SPECIFYPASSWORD_FRAMER; s_specifypassword.framer.generic.flags = QMF_INACTIVE; s_specifypassword.framer.generic.x = 376; s_specifypassword.framer.generic.y = 76; s_specifypassword.framer.width = 256; s_specifypassword.framer.height = 334; s_specifypassword.info.generic.type = MTYPE_TEXT; s_specifypassword.info.generic.x = 320; s_specifypassword.info.generic.y = 160; s_specifypassword.info.color = color_white; s_specifypassword.info.style = UI_CENTER; s_specifypassword.info.string = s_specifypassword.servername; s_specifypassword.password.generic.type = MTYPE_FIELD; s_specifypassword.password.generic.name = "Password:"; s_specifypassword.password.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_specifypassword.password.generic.x = 206; s_specifypassword.password.generic.y = 220; s_specifypassword.password.field.widthInChars = 38; s_specifypassword.password.field.maxchars = 80; trap_Cvar_VariableStringBuffer("password",s_specifypassword.password.field.buffer,80); s_specifypassword.go.generic.type = MTYPE_BITMAP; s_specifypassword.go.generic.name = SPECIFYPASSWORD_FIGHT0; s_specifypassword.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_specifypassword.go.generic.callback = SpecifyPassword_Event; s_specifypassword.go.generic.id = ID_SPECIFYPASSWORDGO; s_specifypassword.go.generic.x = 640; s_specifypassword.go.generic.y = 480-64; s_specifypassword.go.width = 128; s_specifypassword.go.height = 64; s_specifypassword.go.focuspic = SPECIFYPASSWORD_FIGHT1; s_specifypassword.back.generic.type = MTYPE_BITMAP; s_specifypassword.back.generic.name = SPECIFYPASSWORD_BACK0; s_specifypassword.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_specifypassword.back.generic.callback = SpecifyPassword_Event; s_specifypassword.back.generic.id = ID_SPECIFYPASSWORDBACK; s_specifypassword.back.generic.x = 0; s_specifypassword.back.generic.y = 480-64; s_specifypassword.back.width = 128; s_specifypassword.back.height = 64; s_specifypassword.back.focuspic = SPECIFYPASSWORD_BACK1; Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.banner ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.info ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.framel ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.framer ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.password ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.go ); Menu_AddItem( &s_specifypassword.menu, &s_specifypassword.back ); } /* ================= SpecifyPassword_Cache ================= */ void SpecifyPassword_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!specifypassword_artlist[i]) break; trap_R_RegisterShaderNoMip(specifypassword_artlist[i]); } } /* ================= UI_SpecifyPasswordMenu ================= */ void UI_SpecifyPasswordMenu( char* string, char *name ) { SpecifyPassword_MenuInit(); s_specifypassword.connectstring = string; Q_strncpyz(s_specifypassword.servername,name,32); UI_PushMenu( &s_specifypassword.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_splevel.c0000644000175000017500000007410311656310261017475 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= SINGLE PLAYER LEVEL SELECT MENU ============================================================================= */ #include "ui_local.h" #define ART_LEVELFRAME_FOCUS "menu/art_blueish/maps_select" #define ART_LEVELFRAME_SELECTED "menu/art_blueish/maps_selected" #define ART_ARROW "menu/art_blueish/narrow_0" #define ART_ARROW_FOCUS "menu/art_blueish/narrow_1" #define ART_MAP_UNKNOWN "menu/art/unknownmap" #define ART_MAP_COMPLETE1 "menu/art/level_complete1" #define ART_MAP_COMPLETE2 "menu/art/level_complete2" #define ART_MAP_COMPLETE3 "menu/art/level_complete3" #define ART_MAP_COMPLETE4 "menu/art/level_complete4" #define ART_MAP_COMPLETE5 "menu/art/level_complete5" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/fight_0" #define ART_FIGHT1 "menu/art_blueish/fight_1" #define ART_RESET0 "menu/art_blueish/reset_0" #define ART_RESET1 "menu/art_blueish/reset_1" #define ART_CUSTOM0 "menu/art_blueish/skirmish_0" #define ART_CUSTOM1 "menu/art_blueish/skirmish_1" #define ID_LEFTARROW 10 #define ID_PICTURE0 11 #define ID_PICTURE1 12 #define ID_PICTURE2 13 #define ID_PICTURE3 14 #define ID_RIGHTARROW 15 #define ID_PLAYERPIC 16 #define ID_AWARD1 17 #define ID_AWARD2 18 #define ID_AWARD3 19 #define ID_AWARD4 20 #define ID_AWARD5 21 #define ID_AWARD6 22 #define ID_BACK 23 #define ID_RESET 24 #define ID_CUSTOM 25 #define ID_NEXT 26 #define PLAYER_Y 314 #define AWARDS_Y (PLAYER_Y + 26) typedef struct { menuframework_s menu; menutext_s item_banner; menubitmap_s item_leftarrow; menubitmap_s item_maps[4]; menubitmap_s item_rightarrow; menubitmap_s item_player; menubitmap_s item_awards[6]; menubitmap_s item_back; menubitmap_s item_reset; menubitmap_s item_custom; menubitmap_s item_next; menubitmap_s item_null; qboolean reinit; const char * selectedArenaInfo; int numMaps; char levelPicNames[4][MAX_QPATH]; char levelNames[4][16]; int levelScores[4]; int levelScoresSkill[4]; qhandle_t levelSelectedPic; qhandle_t levelFocusPic; qhandle_t levelCompletePic[5]; char playerModel[MAX_QPATH]; char playerPicName[MAX_QPATH]; int awardLevels[6]; sfxHandle_t awardSounds[6]; int numBots; qhandle_t botPics[7]; char botNames[7][10]; } levelMenuInfo_t; static levelMenuInfo_t levelMenuInfo; static int selectedArenaSet; static int selectedArena; static int currentSet; static int currentGame; static int trainingTier; static int finalTier; static int minTier; static int maxTier; /* ================= PlayerIcon ================= */ static void PlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) { char *skin; char model[MAX_QPATH]; Q_strncpyz( model, modelAndSkin, sizeof(model)); skin = strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = "default"; } Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin ); if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) { Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model ); } } /* ================= PlayerIconhandle ================= */ static qhandle_t PlayerIconHandle( const char *modelAndSkin ) { char iconName[MAX_QPATH]; PlayerIcon( modelAndSkin, iconName, sizeof(iconName) ); return trap_R_RegisterShaderNoMip( iconName ); } /* ================= UI_SPLevelMenu_SetBots ================= */ static void UI_SPLevelMenu_SetBots( void ) { char *p; char *bot; char *botInfo; char bots[MAX_INFO_STRING]; levelMenuInfo.numBots = 0; if ( selectedArenaSet > currentSet ) { return; } Q_strncpyz( bots, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "bots" ), sizeof(bots) ); p = &bots[0]; while( *p && levelMenuInfo.numBots < 7 ) { //skip spaces while( *p && *p == ' ' ) { p++; } if( !p ) { break; } // mark start of bot name bot = p; // skip until space of null while( *p && *p != ' ' ) { p++; } if( *p ) { *p++ = 0; } botInfo = UI_GetBotInfoByName( bot ); if( !botInfo ) { botInfo = UI_GetBotInfoByNumber( levelMenuInfo.numBots ); } if( botInfo ) { levelMenuInfo.botPics[levelMenuInfo.numBots] = PlayerIconHandle( Info_ValueForKey( botInfo, "model" ) ); Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], Info_ValueForKey( botInfo, "name" ), 10 ); } else { levelMenuInfo.botPics[levelMenuInfo.numBots] = 0; Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], bot, 10 ); } Q_CleanStr( levelMenuInfo.botNames[levelMenuInfo.numBots] ); levelMenuInfo.numBots++; } } /* ================= UI_SPLevelMenu_SetMenuItems ================= */ static void UI_SPLevelMenu_SetMenuArena( int n, int level, const char *arenaInfo ) { char map[MAX_QPATH]; Q_strncpyz( map, Info_ValueForKey( arenaInfo, "map" ), sizeof(map) ); Q_strncpyz( levelMenuInfo.levelNames[n], map, sizeof(levelMenuInfo.levelNames[n]) ); Q_strupr( levelMenuInfo.levelNames[n] ); UI_GetBestScore( level, &levelMenuInfo.levelScores[n], &levelMenuInfo.levelScoresSkill[n] ); if( levelMenuInfo.levelScores[n] > 8 ) { levelMenuInfo.levelScores[n] = 8; } strcpy( levelMenuInfo.levelPicNames[n], va( "levelshots/%s.tga", map ) ); if( !trap_R_RegisterShaderNoMip( levelMenuInfo.levelPicNames[n] ) ) { strcpy( levelMenuInfo.levelPicNames[n], ART_MAP_UNKNOWN ); } levelMenuInfo.item_maps[n].shader = 0; if ( selectedArenaSet > currentSet ) { levelMenuInfo.item_maps[n].generic.flags |= QMF_GRAYED; } else { levelMenuInfo.item_maps[n].generic.flags &= ~QMF_GRAYED; } levelMenuInfo.item_maps[n].generic.flags &= ~QMF_INACTIVE; } static void UI_SPLevelMenu_SetMenuItems( void ) { int n; int level; const char *arenaInfo; if ( selectedArenaSet > currentSet ) { selectedArena = -1; } else if ( selectedArena == -1 ) { selectedArena = 0; } if( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) { selectedArena = 0; } if( selectedArena != -1 ) { trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena ); } if( selectedArenaSet == trainingTier ) { arenaInfo = UI_GetSpecialArenaInfo( "training" ); level = atoi( Info_ValueForKey( arenaInfo, "num" ) ); UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo ); levelMenuInfo.selectedArenaInfo = arenaInfo; levelMenuInfo.item_maps[0].generic.x = 256; Bitmap_Init( &levelMenuInfo.item_maps[0] ); levelMenuInfo.item_maps[0].generic.bottom += 32; levelMenuInfo.numMaps = 1; levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE; levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE; levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE; levelMenuInfo.levelPicNames[1][0] = 0; levelMenuInfo.levelPicNames[2][0] = 0; levelMenuInfo.levelPicNames[3][0] = 0; levelMenuInfo.item_maps[1].shader = 0; levelMenuInfo.item_maps[2].shader = 0; levelMenuInfo.item_maps[3].shader = 0; } else if( selectedArenaSet == finalTier ) { arenaInfo = UI_GetSpecialArenaInfo( "final" ); level = atoi( Info_ValueForKey( arenaInfo, "num" ) ); UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo ); levelMenuInfo.selectedArenaInfo = arenaInfo; levelMenuInfo.item_maps[0].generic.x = 256; Bitmap_Init( &levelMenuInfo.item_maps[0] ); levelMenuInfo.item_maps[0].generic.bottom += 32; levelMenuInfo.numMaps = 1; levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE; levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE; levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE; levelMenuInfo.levelPicNames[1][0] = 0; levelMenuInfo.levelPicNames[2][0] = 0; levelMenuInfo.levelPicNames[3][0] = 0; levelMenuInfo.item_maps[1].shader = 0; levelMenuInfo.item_maps[2].shader = 0; levelMenuInfo.item_maps[3].shader = 0; } else { levelMenuInfo.item_maps[0].generic.x = 46; Bitmap_Init( &levelMenuInfo.item_maps[0] ); levelMenuInfo.item_maps[0].generic.bottom += 18; levelMenuInfo.numMaps = 4; for ( n = 0; n < 4; n++ ) { level = selectedArenaSet * ARENAS_PER_TIER + n; arenaInfo = UI_GetArenaInfoByNumber( level ); UI_SPLevelMenu_SetMenuArena( n, level, arenaInfo ); } if( selectedArena != -1 ) { levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena ); } } // enable/disable arrows when they are valid/invalid if ( selectedArenaSet == minTier ) { levelMenuInfo.item_leftarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN ); } else { levelMenuInfo.item_leftarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN ); } if ( selectedArenaSet == maxTier ) { levelMenuInfo.item_rightarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN ); } else { levelMenuInfo.item_rightarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN ); } UI_SPLevelMenu_SetBots(); } /* ================= UI_SPLevelMenu_ResetEvent ================= */ static void UI_SPLevelMenu_ResetDraw( void ) { UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This resets all of the", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "single player game variables.", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 2, "Do this only if you want to", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 3, "start over from the beginning.", UI_CENTER|UI_SMALLFONT, color_yellow ); } static void UI_SPLevelMenu_ResetAction( qboolean result ) { if( !result ) { return; } // clear game variables UI_NewGame(); trap_Cvar_SetValue( "ui_spSelection", -4 ); // make the level select menu re-initialize UI_PopMenu(); UI_SPLevelMenu(); } static void UI_SPLevelMenu_ResetEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } UI_ConfirmMenu( "RESET GAME?", UI_SPLevelMenu_ResetDraw, UI_SPLevelMenu_ResetAction ); } /* ================= UI_SPLevelMenu_LevelEvent ================= */ static void UI_SPLevelMenu_LevelEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } if ( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) { return; } selectedArena = ((menucommon_s*)ptr)->id - ID_PICTURE0; levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena ); UI_SPLevelMenu_SetBots(); trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena ); } /* ================= UI_SPLevelMenu_LeftArrowEvent ================= */ static void UI_SPLevelMenu_LeftArrowEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } if ( selectedArenaSet == minTier ) { return; } selectedArenaSet--; UI_SPLevelMenu_SetMenuItems(); } /* ================= UI_SPLevelMenu_RightArrowEvent ================= */ static void UI_SPLevelMenu_RightArrowEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } if ( selectedArenaSet == maxTier ) { return; } selectedArenaSet++; UI_SPLevelMenu_SetMenuItems(); } /* ================= UI_SPLevelMenu_PlayerEvent ================= */ static void UI_SPLevelMenu_PlayerEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } UI_PlayerSettingsMenu(); } /* ================= UI_SPLevelMenu_AwardEvent ================= */ static void UI_SPLevelMenu_AwardEvent( void* ptr, int notification ) { int n; if (notification != QM_ACTIVATED) { return; } n = ((menucommon_s*)ptr)->id - ID_AWARD1; trap_S_StartLocalSound( levelMenuInfo.awardSounds[n], CHAN_ANNOUNCER ); } /* ================= UI_SPLevelMenu_NextEvent ================= */ static void UI_SPLevelMenu_NextEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } if ( selectedArenaSet > currentSet ) { return; } if ( selectedArena == -1 ) { selectedArena = 0; } UI_SPSkillMenu( levelMenuInfo.selectedArenaInfo ); } /* ================= UI_SPLevelMenu_BackEvent ================= */ static void UI_SPLevelMenu_BackEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } if ( selectedArena == -1 ) { selectedArena = 0; } UI_PopMenu(); } /* ================= UI_SPLevelMenu_CustomEvent ================= */ static void UI_SPLevelMenu_CustomEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } UI_StartServerMenu( qfalse ); } /* ================= UI_SPLevelMenu_MenuDraw ================= */ #define LEVEL_DESC_LEFT_MARGIN 332 static void UI_SPLevelMenu_MenuDraw( void ) { int n, i; int x, y; vec4_t color; int level; // int fraglimit; int pad; char buf[MAX_INFO_VALUE]; char string[64]; if( levelMenuInfo.reinit ) { UI_PopMenu(); UI_SPLevelMenu(); return; } // draw player name trap_Cvar_VariableStringBuffer( "name", string, 32 ); Q_CleanStr( string ); UI_DrawProportionalString( 320, PLAYER_Y, string, UI_CENTER|UI_SMALLFONT, color_orange ); // check for model changes trap_Cvar_VariableStringBuffer( "model", buf, sizeof(buf) ); if( Q_stricmp( buf, levelMenuInfo.playerModel ) != 0 ) { Q_strncpyz( levelMenuInfo.playerModel, buf, sizeof(levelMenuInfo.playerModel) ); PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) ); levelMenuInfo.item_player.shader = 0; } // standard menu drawing Menu_Draw( &levelMenuInfo.menu ); // draw player award levels y = AWARDS_Y; i = 0; for( n = 0; n < 6; n++ ) { level = levelMenuInfo.awardLevels[n]; if( level > 0 ) { if( i & 1 ) { x = 224 - (i - 1 ) / 2 * (48 + 16); } else { x = 368 + i / 2 * (48 + 16); } i++; if( level == 1 ) { continue; } if( level >= 1000000 ) { Com_sprintf( string, sizeof(string), "%im", level / 1000000 ); } else if( level >= 1000 ) { Com_sprintf( string, sizeof(string), "%ik", level / 1000 ); } else { Com_sprintf( string, sizeof(string), "%i", level ); } UI_DrawString( x + 24, y + 48, string, UI_CENTER, color_yellow ); } } UI_DrawProportionalString( 18, 38, va( "Tier %i", selectedArenaSet + 1 ), UI_LEFT|UI_SMALLFONT, color_orange ); for ( n = 0; n < levelMenuInfo.numMaps; n++ ) { x = levelMenuInfo.item_maps[n].generic.x; y = levelMenuInfo.item_maps[n].generic.y; UI_FillRect( x, y + 96, 128, 18, color_black ); } if ( selectedArenaSet > currentSet ) { UI_DrawProportionalString( 320, 216, "ACCESS DENIED", UI_CENTER|UI_BIGFONT, color_red ); return; } // show levelshots for levels of current tier Vector4Copy( color_white, color ); color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR); for ( n = 0; n < levelMenuInfo.numMaps; n++ ) { x = levelMenuInfo.item_maps[n].generic.x; y = levelMenuInfo.item_maps[n].generic.y; UI_DrawString( x + 64, y + 96, levelMenuInfo.levelNames[n], UI_CENTER|UI_SMALLFONT, color_orange ); if( levelMenuInfo.levelScores[n] == 1 ) { UI_DrawHandlePic( x, y, 128, 96, levelMenuInfo.levelCompletePic[levelMenuInfo.levelScoresSkill[n] - 1] ); } if ( n == selectedArena ) { if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) { trap_R_SetColor( color ); } UI_DrawHandlePic( x-1, y-1, 130, 130 - 14, levelMenuInfo.levelSelectedPic ); trap_R_SetColor( NULL ); } else if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) { trap_R_SetColor( color ); UI_DrawHandlePic( x-31, y-30, 256, 256-27, levelMenuInfo.levelFocusPic); trap_R_SetColor( NULL ); } } // show map name and long name of selected level y = 192; Q_strncpyz( buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "map" ), 20 ); Q_strupr( buf ); Com_sprintf( string, sizeof(string), "%s: %s", buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "longname" ) ); UI_DrawProportionalString( 320, y, string, UI_CENTER|UI_SMALLFONT, color_orange ); // fraglimit = atoi( Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "fraglimit" ) ); // UI_DrawString( 18, 212, va("Frags %i", fraglimit) , UI_LEFT|UI_SMALLFONT, color_orange ); // draw bot opponents y += 24; pad = (7 - levelMenuInfo.numBots) * (64 + 26) / 2; for( n = 0; n < levelMenuInfo.numBots; n++ ) { x = 18 + pad + (64 + 26) * n; if( levelMenuInfo.botPics[n] ) { UI_DrawHandlePic( x, y, 64, 64, levelMenuInfo.botPics[n]); } else { UI_FillRect( x, y, 64, 64, color_black ); UI_DrawProportionalString( x+22, y+18, "?", UI_BIGFONT, color_orange ); } UI_DrawString( x, y + 64, levelMenuInfo.botNames[n], UI_SMALLFONT|UI_LEFT, color_orange ); } } /* ================= UI_SPLevelMenu_Cache ================= */ void UI_SPLevelMenu_Cache( void ) { int n; trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS ); trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED ); trap_R_RegisterShaderNoMip( ART_ARROW ); trap_R_RegisterShaderNoMip( ART_ARROW_FOCUS ); trap_R_RegisterShaderNoMip( ART_MAP_UNKNOWN ); trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 ); trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 ); trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 ); trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 ); trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_RESET0 ); trap_R_RegisterShaderNoMip( ART_RESET1 ); trap_R_RegisterShaderNoMip( ART_CUSTOM0 ); trap_R_RegisterShaderNoMip( ART_CUSTOM1 ); for( n = 0; n < 6; n++ ) { trap_R_RegisterShaderNoMip( ui_medalPicNames[n] ); levelMenuInfo.awardSounds[n] = trap_S_RegisterSound( ui_medalSounds[n], qfalse ); } levelMenuInfo.levelSelectedPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED ); levelMenuInfo.levelFocusPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS ); levelMenuInfo.levelCompletePic[0] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 ); levelMenuInfo.levelCompletePic[1] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 ); levelMenuInfo.levelCompletePic[2] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 ); levelMenuInfo.levelCompletePic[3] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 ); levelMenuInfo.levelCompletePic[4] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 ); } /* ================= UI_SPLevelMenu_Init ================= */ static void UI_SPLevelMenu_Init( void ) { int skill; int n; int x, y; int count; char buf[MAX_QPATH]; skill = (int)trap_Cvar_VariableValue( "g_spSkill" ); if( skill < 1 || skill > 5 ) { trap_Cvar_Set( "g_spSkill", "2" ); skill = 2; } memset( &levelMenuInfo, 0, sizeof(levelMenuInfo) ); levelMenuInfo.menu.fullscreen = qtrue; levelMenuInfo.menu.wrapAround = qtrue; levelMenuInfo.menu.draw = UI_SPLevelMenu_MenuDraw; UI_SPLevelMenu_Cache(); levelMenuInfo.item_banner.generic.type = MTYPE_BTEXT; levelMenuInfo.item_banner.generic.x = 320; levelMenuInfo.item_banner.generic.y = 16; levelMenuInfo.item_banner.string = "CHOOSE LEVEL"; levelMenuInfo.item_banner.color = color_red; levelMenuInfo.item_banner.style = UI_CENTER; levelMenuInfo.item_leftarrow.generic.type = MTYPE_BITMAP; levelMenuInfo.item_leftarrow.generic.name = ART_ARROW; levelMenuInfo.item_leftarrow.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_leftarrow.generic.x = 18; levelMenuInfo.item_leftarrow.generic.y = 64; levelMenuInfo.item_leftarrow.generic.callback = UI_SPLevelMenu_LeftArrowEvent; levelMenuInfo.item_leftarrow.generic.id = ID_LEFTARROW; levelMenuInfo.item_leftarrow.width = 16; levelMenuInfo.item_leftarrow.height = 114; levelMenuInfo.item_leftarrow.focuspic = ART_ARROW_FOCUS; levelMenuInfo.item_maps[0].generic.type = MTYPE_BITMAP; levelMenuInfo.item_maps[0].generic.name = levelMenuInfo.levelPicNames[0]; levelMenuInfo.item_maps[0].generic.flags = QMF_LEFT_JUSTIFY; levelMenuInfo.item_maps[0].generic.x = 46; levelMenuInfo.item_maps[0].generic.y = 64; levelMenuInfo.item_maps[0].generic.id = ID_PICTURE0; levelMenuInfo.item_maps[0].generic.callback = UI_SPLevelMenu_LevelEvent; levelMenuInfo.item_maps[0].width = 128; levelMenuInfo.item_maps[0].height = 96; levelMenuInfo.item_maps[1].generic.type = MTYPE_BITMAP; levelMenuInfo.item_maps[1].generic.name = levelMenuInfo.levelPicNames[1]; levelMenuInfo.item_maps[1].generic.flags = QMF_LEFT_JUSTIFY; levelMenuInfo.item_maps[1].generic.x = 186; levelMenuInfo.item_maps[1].generic.y = 64; levelMenuInfo.item_maps[1].generic.id = ID_PICTURE1; levelMenuInfo.item_maps[1].generic.callback = UI_SPLevelMenu_LevelEvent; levelMenuInfo.item_maps[1].width = 128; levelMenuInfo.item_maps[1].height = 96; levelMenuInfo.item_maps[2].generic.type = MTYPE_BITMAP; levelMenuInfo.item_maps[2].generic.name = levelMenuInfo.levelPicNames[2]; levelMenuInfo.item_maps[2].generic.flags = QMF_LEFT_JUSTIFY; levelMenuInfo.item_maps[2].generic.x = 326; levelMenuInfo.item_maps[2].generic.y = 64; levelMenuInfo.item_maps[2].generic.id = ID_PICTURE2; levelMenuInfo.item_maps[2].generic.callback = UI_SPLevelMenu_LevelEvent; levelMenuInfo.item_maps[2].width = 128; levelMenuInfo.item_maps[2].height = 96; levelMenuInfo.item_maps[3].generic.type = MTYPE_BITMAP; levelMenuInfo.item_maps[3].generic.name = levelMenuInfo.levelPicNames[3]; levelMenuInfo.item_maps[3].generic.flags = QMF_LEFT_JUSTIFY; levelMenuInfo.item_maps[3].generic.x = 466; levelMenuInfo.item_maps[3].generic.y = 64; levelMenuInfo.item_maps[3].generic.id = ID_PICTURE3; levelMenuInfo.item_maps[3].generic.callback = UI_SPLevelMenu_LevelEvent; levelMenuInfo.item_maps[3].width = 128; levelMenuInfo.item_maps[3].height = 96; levelMenuInfo.item_rightarrow.generic.type = MTYPE_BITMAP; levelMenuInfo.item_rightarrow.generic.name = ART_ARROW; levelMenuInfo.item_rightarrow.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_rightarrow.generic.x = 606; levelMenuInfo.item_rightarrow.generic.y = 64; levelMenuInfo.item_rightarrow.generic.callback = UI_SPLevelMenu_RightArrowEvent; levelMenuInfo.item_rightarrow.generic.id = ID_RIGHTARROW; levelMenuInfo.item_rightarrow.width = -16; levelMenuInfo.item_rightarrow.height = 114; levelMenuInfo.item_rightarrow.focuspic = ART_ARROW_FOCUS; trap_Cvar_VariableStringBuffer( "model", levelMenuInfo.playerModel, sizeof(levelMenuInfo.playerModel) ); PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) ); levelMenuInfo.item_player.generic.type = MTYPE_BITMAP; levelMenuInfo.item_player.generic.name = levelMenuInfo.playerPicName; levelMenuInfo.item_player.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY; levelMenuInfo.item_player.generic.x = 288; levelMenuInfo.item_player.generic.y = AWARDS_Y; levelMenuInfo.item_player.generic.id = ID_PLAYERPIC; levelMenuInfo.item_player.generic.callback = UI_SPLevelMenu_PlayerEvent; levelMenuInfo.item_player.width = 64; levelMenuInfo.item_player.height = 64; for( n = 0; n < 6; n++ ) { levelMenuInfo.awardLevels[n] = UI_GetAwardLevel( n ); } levelMenuInfo.awardLevels[AWARD_FRAGS] = 100 * (levelMenuInfo.awardLevels[AWARD_FRAGS] / 100); y = AWARDS_Y; count = 0; for( n = 0; n < 6; n++ ) { if( levelMenuInfo.awardLevels[n] ) { if( count & 1 ) { x = 224 - (count - 1 ) / 2 * (48 + 16); } else { x = 368 + count / 2 * (48 + 16); } levelMenuInfo.item_awards[count].generic.type = MTYPE_BITMAP; levelMenuInfo.item_awards[count].generic.name = ui_medalPicNames[n]; levelMenuInfo.item_awards[count].generic.flags = QMF_LEFT_JUSTIFY|QMF_SILENT|QMF_MOUSEONLY; levelMenuInfo.item_awards[count].generic.x = x; levelMenuInfo.item_awards[count].generic.y = y; levelMenuInfo.item_awards[count].generic.id = ID_AWARD1 + n; levelMenuInfo.item_awards[count].generic.callback = UI_SPLevelMenu_AwardEvent; levelMenuInfo.item_awards[count].width = 48; levelMenuInfo.item_awards[count].height = 48; count++; } } levelMenuInfo.item_back.generic.type = MTYPE_BITMAP; levelMenuInfo.item_back.generic.name = ART_BACK0; levelMenuInfo.item_back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_back.generic.x = 0; levelMenuInfo.item_back.generic.y = 480-64; levelMenuInfo.item_back.generic.callback = UI_SPLevelMenu_BackEvent; levelMenuInfo.item_back.generic.id = ID_BACK; levelMenuInfo.item_back.width = 128; levelMenuInfo.item_back.height = 64; levelMenuInfo.item_back.focuspic = ART_BACK1; levelMenuInfo.item_reset.generic.type = MTYPE_BITMAP; levelMenuInfo.item_reset.generic.name = ART_RESET0; levelMenuInfo.item_reset.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_reset.generic.x = 170; levelMenuInfo.item_reset.generic.y = 480-64; levelMenuInfo.item_reset.generic.callback = UI_SPLevelMenu_ResetEvent; levelMenuInfo.item_reset.generic.id = ID_RESET; levelMenuInfo.item_reset.width = 128; levelMenuInfo.item_reset.height = 64; levelMenuInfo.item_reset.focuspic = ART_RESET1; levelMenuInfo.item_custom.generic.type = MTYPE_BITMAP; levelMenuInfo.item_custom.generic.name = ART_CUSTOM0; levelMenuInfo.item_custom.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_custom.generic.x = 342; levelMenuInfo.item_custom.generic.y = 480-64; levelMenuInfo.item_custom.generic.callback = UI_SPLevelMenu_CustomEvent; levelMenuInfo.item_custom.generic.id = ID_CUSTOM; levelMenuInfo.item_custom.width = 128; levelMenuInfo.item_custom.height = 64; levelMenuInfo.item_custom.focuspic = ART_CUSTOM1; levelMenuInfo.item_next.generic.type = MTYPE_BITMAP; levelMenuInfo.item_next.generic.name = ART_FIGHT0; levelMenuInfo.item_next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; levelMenuInfo.item_next.generic.x = 640; levelMenuInfo.item_next.generic.y = 480-64; levelMenuInfo.item_next.generic.callback = UI_SPLevelMenu_NextEvent; levelMenuInfo.item_next.generic.id = ID_NEXT; levelMenuInfo.item_next.width = 128; levelMenuInfo.item_next.height = 64; levelMenuInfo.item_next.focuspic = ART_FIGHT1; levelMenuInfo.item_null.generic.type = MTYPE_BITMAP; levelMenuInfo.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT; levelMenuInfo.item_null.generic.x = 0; levelMenuInfo.item_null.generic.y = 0; levelMenuInfo.item_null.width = 640; levelMenuInfo.item_null.height = 480; Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_banner ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_leftarrow ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[0] ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[1] ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[2] ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[3] ); levelMenuInfo.item_maps[0].generic.bottom += 18; levelMenuInfo.item_maps[1].generic.bottom += 18; levelMenuInfo.item_maps[2].generic.bottom += 18; levelMenuInfo.item_maps[3].generic.bottom += 18; Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_rightarrow ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_player ); for( n = 0; n < count; n++ ) { Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_awards[n] ); } Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_back ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_reset ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_custom ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_next ); Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_null ); trap_Cvar_VariableStringBuffer( "ui_spSelection", buf, sizeof(buf) ); if( *buf ) { n = atoi( buf ); selectedArenaSet = n / ARENAS_PER_TIER; selectedArena = n % ARENAS_PER_TIER; } else { selectedArenaSet = currentSet; selectedArena = currentGame; } UI_SPLevelMenu_SetMenuItems(); } /* ================= UI_SPLevelMenu ================= */ void UI_SPLevelMenu( void ) { int level; int trainingLevel; const char *arenaInfo; trainingTier = -1; arenaInfo = UI_GetSpecialArenaInfo( "training" ); if( arenaInfo ) { minTier = trainingTier; trainingLevel = atoi( Info_ValueForKey( arenaInfo, "num" ) ); } else { minTier = 0; trainingLevel = -2; } finalTier = UI_GetNumSPTiers(); arenaInfo = UI_GetSpecialArenaInfo( "final" ); if( arenaInfo ) { maxTier = finalTier; } else { maxTier = finalTier - 1; if( maxTier < minTier ) { maxTier = minTier; } } level = UI_GetCurrentGame(); if ( level == -1 ) { level = UI_GetNumSPArenas() - 1; if( maxTier == finalTier ) { level++; } } if( level == trainingLevel ) { currentSet = -1; currentGame = 0; } else { currentSet = level / ARENAS_PER_TIER; currentGame = level % ARENAS_PER_TIER; } UI_SPLevelMenu_Init(); UI_PushMenu( &levelMenuInfo.menu ); Menu_SetCursorToItem( &levelMenuInfo.menu, &levelMenuInfo.item_next ); } /* ================= UI_SPLevelMenu_f ================= */ void UI_SPLevelMenu_f( void ) { trap_Key_SetCatcher( KEYCATCH_UI ); uis.menusp = 0; UI_SPLevelMenu(); } /* ================= UI_SPLevelMenu_ReInit ================= */ void UI_SPLevelMenu_ReInit( void ) { levelMenuInfo.reinit = qtrue; } openarena_0.8.8.orig/code/q3_ui/q3_ui.q3asm0000644000175000017500000000122711656310261017145 0ustar smcvsmcv-o "..\..\vm\ui" ui_main ..\ui\ui_syscalls ui_gameinfo ui_atoms ui_challenges ui_cinematics ui_connect ui_controls2 ui_demo2 ui_mfield ui_credits ui_menu ui_ingame ui_confirm ui_setup ui_options ui_display ui_sound ui_network ui_password ui_playermodel ui_players ui_playersettings ui_preferences ui_qmenu ui_serverinfo ui_servers2 ui_sparena ui_specifyserver ui_sppostgame ui_splevel ui_spskill ui_startserver ui_team ui_video ui_addbots ui_removebots ui_teamorders ui_cdkey ui_mods ui_votemenu ui_votemenu_fraglimit ui_votemenu_gametype ui_votemenu_kick ui_votemenu_map ui_votemenu_timelimit ..\game\bg_misc ..\game\bg_lib ..\qcommon\q_math ..\qcommon\q_shared openarena_0.8.8.orig/code/q3_ui/ui_team.c0000644000175000017500000001375311656310261016755 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // ui_team.c // #include "ui_local.h" #define TEAMMAIN_FRAME "menu/art_blueish/cut_frame" #define ID_JOINRED 100 #define ID_JOINBLUE 101 #define ID_JOINGAME 102 #define ID_SPECTATE 103 typedef struct { menuframework_s menu; menubitmap_s frame; menutext_s joinred; menutext_s joinblue; menutext_s joingame; menutext_s spectate; } teammain_t; static teammain_t s_teammain; // bk001204 - unused //static menuframework_s s_teammain_menu; //static menuaction_s s_teammain_orders; //static menuaction_s s_teammain_voice; //static menuaction_s s_teammain_joinred; //static menuaction_s s_teammain_joinblue; //static menuaction_s s_teammain_joingame; //static menuaction_s s_teammain_spectate; /* =============== TeamMain_MenuEvent =============== */ static void TeamMain_MenuEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_JOINRED: trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team red\n" ); UI_ForceMenuOff(); break; case ID_JOINBLUE: trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team blue\n" ); UI_ForceMenuOff(); break; case ID_JOINGAME: trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team free\n" ); UI_ForceMenuOff(); break; case ID_SPECTATE: trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team spectator\n" ); UI_ForceMenuOff(); break; } } /* =============== TeamMain_MenuInit =============== */ void TeamMain_MenuInit( void ) { int y; int gametype; char info[MAX_INFO_STRING]; memset( &s_teammain, 0, sizeof(s_teammain) ); TeamMain_Cache(); s_teammain.menu.wrapAround = qtrue; s_teammain.menu.fullscreen = qfalse; s_teammain.frame.generic.type = MTYPE_BITMAP; s_teammain.frame.generic.flags = QMF_INACTIVE; s_teammain.frame.generic.name = TEAMMAIN_FRAME; s_teammain.frame.generic.x = 142; s_teammain.frame.generic.y = 118; s_teammain.frame.width = 359; s_teammain.frame.height = 256; y = 194; s_teammain.joinred.generic.type = MTYPE_PTEXT; s_teammain.joinred.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_teammain.joinred.generic.id = ID_JOINRED; s_teammain.joinred.generic.callback = TeamMain_MenuEvent; s_teammain.joinred.generic.x = 320; s_teammain.joinred.generic.y = y; s_teammain.joinred.string = "JOIN RED"; s_teammain.joinred.style = UI_CENTER|UI_SMALLFONT; s_teammain.joinred.color = colorRed; y += 20; s_teammain.joinblue.generic.type = MTYPE_PTEXT; s_teammain.joinblue.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_teammain.joinblue.generic.id = ID_JOINBLUE; s_teammain.joinblue.generic.callback = TeamMain_MenuEvent; s_teammain.joinblue.generic.x = 320; s_teammain.joinblue.generic.y = y; s_teammain.joinblue.string = "JOIN BLUE"; s_teammain.joinblue.style = UI_CENTER|UI_SMALLFONT; s_teammain.joinblue.color = colorRed; y += 20; s_teammain.joingame.generic.type = MTYPE_PTEXT; s_teammain.joingame.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_teammain.joingame.generic.id = ID_JOINGAME; s_teammain.joingame.generic.callback = TeamMain_MenuEvent; s_teammain.joingame.generic.x = 320; s_teammain.joingame.generic.y = y; s_teammain.joingame.string = "JOIN GAME"; s_teammain.joingame.style = UI_CENTER|UI_SMALLFONT; s_teammain.joingame.color = colorRed; y += 20; s_teammain.spectate.generic.type = MTYPE_PTEXT; s_teammain.spectate.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_teammain.spectate.generic.id = ID_SPECTATE; s_teammain.spectate.generic.callback = TeamMain_MenuEvent; s_teammain.spectate.generic.x = 320; s_teammain.spectate.generic.y = y; s_teammain.spectate.string = "SPECTATE"; s_teammain.spectate.style = UI_CENTER|UI_SMALLFONT; s_teammain.spectate.color = colorRed; y += 20; trap_GetConfigString(CS_SERVERINFO, info, MAX_INFO_STRING); gametype = atoi( Info_ValueForKey( info,"g_gametype" ) ); // set initial states switch( gametype ) { case GT_SINGLE_PLAYER: case GT_FFA: case GT_LMS: case GT_TOURNAMENT: s_teammain.joinred.generic.flags |= QMF_GRAYED; s_teammain.joinblue.generic.flags |= QMF_GRAYED; break; default: case GT_TEAM: case GT_CTF: case GT_ELIMINATION: case GT_CTF_ELIMINATION: //s_teammain.joingame.generic.flags |= QMF_GRAYED; s_teammain.joingame.string = "AUTO JOIN GAME"; break; } Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.frame ); Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joinred ); Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joinblue ); Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joingame ); Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.spectate ); } /* =============== TeamMain_Cache =============== */ void TeamMain_Cache( void ) { trap_R_RegisterShaderNoMip( TEAMMAIN_FRAME ); } /* =============== UI_TeamMainMenu =============== */ void UI_TeamMainMenu( void ) { TeamMain_MenuInit(); UI_PushMenu ( &s_teammain.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_preferences.c0000644000175000017500000006102011656310261020316 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= GAME OPTIONS MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define PREFERENCES_X_POS 360 #define ID_CROSSHAIR 127 #define ID_SIMPLEITEMS 128 #define ID_HIGHQUALITYSKY 129 #define ID_EJECTINGBRASS 130 #define ID_WALLMARKS 131 #define ID_DYNAMICLIGHTS 132 #define ID_IDENTIFYTARGET 133 #define ID_SYNCEVERYFRAME 134 #define ID_FORCEMODEL 135 #define ID_DRAWTEAMOVERLAY 136 #define ID_ALLOWDOWNLOAD 137 #define ID_BACK 138 //Elimination #define ID_WEAPONBAR 139 #define ID_DELAGHITSCAN 140 #define ID_COLORRED 141 #define ID_COLORGREEN 142 #define ID_COLORBLUE 143 #define ID_CROSSHAIRHEALTH 144 #define ID_CHATBEEP 145 #define ID_TEAMCHATBEEP 146 #define NUM_CROSSHAIRS 99 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menulist_s crosshair; menuradiobutton_s crosshairHealth; //Crosshair colors: menuslider_s crosshairColorRed; menuslider_s crosshairColorGreen; menuslider_s crosshairColorBlue; menuradiobutton_s simpleitems; menuradiobutton_s alwaysweaponbar; menuradiobutton_s brass; menuradiobutton_s wallmarks; menuradiobutton_s dynamiclights; menuradiobutton_s identifytarget; menuradiobutton_s highqualitysky; menuradiobutton_s synceveryframe; menuradiobutton_s forcemodel; menulist_s drawteamoverlay; menuradiobutton_s delaghitscan; menuradiobutton_s allowdownload; menuradiobutton_s chatbeep; menuradiobutton_s teamchatbeep; menubitmap_s back; qhandle_t crosshairShader[NUM_CROSSHAIRS]; } preferences_t; static preferences_t s_preferences; static const char *teamoverlay_names[] = { "off", "upper right", "lower right", "lower left", NULL }; static void Preferences_SetMenuItems( void ) { s_preferences.crosshair.curvalue = (int)trap_Cvar_VariableValue( "cg_drawCrosshair" ) % NUM_CROSSHAIRS; s_preferences.crosshairHealth.curvalue = trap_Cvar_VariableValue( "cg_crosshairHealth") != 0; s_preferences.crosshairColorRed.curvalue = trap_Cvar_VariableValue( "cg_crosshairColorRed")*255.0f; s_preferences.crosshairColorGreen.curvalue = trap_Cvar_VariableValue( "cg_crosshairColorGreen")*255.0f; s_preferences.crosshairColorBlue.curvalue = trap_Cvar_VariableValue( "cg_crosshairColorBlue")*255.0f; s_preferences.simpleitems.curvalue = trap_Cvar_VariableValue( "cg_simpleItems" ) != 0; s_preferences.alwaysweaponbar.curvalue = trap_Cvar_VariableValue( "cg_alwaysWeaponBar" ) != 0; s_preferences.brass.curvalue = trap_Cvar_VariableValue( "cg_brassTime" ) != 0; s_preferences.wallmarks.curvalue = trap_Cvar_VariableValue( "cg_marks" ) != 0; s_preferences.identifytarget.curvalue = trap_Cvar_VariableValue( "cg_drawCrosshairNames" ) != 0; s_preferences.dynamiclights.curvalue = trap_Cvar_VariableValue( "r_dynamiclight" ) != 0; s_preferences.highqualitysky.curvalue = trap_Cvar_VariableValue ( "r_fastsky" ) == 0; s_preferences.synceveryframe.curvalue = trap_Cvar_VariableValue( "r_finish" ) != 0; s_preferences.forcemodel.curvalue = trap_Cvar_VariableValue( "cg_forcemodel" ) != 0; s_preferences.drawteamoverlay.curvalue = Com_Clamp( 0, 3, trap_Cvar_VariableValue( "cg_drawTeamOverlay" ) ); s_preferences.allowdownload.curvalue = trap_Cvar_VariableValue( "cl_allowDownload" ) != 0; s_preferences.delaghitscan.curvalue = trap_Cvar_VariableValue( "cg_delag" ) != 0; s_preferences.chatbeep.curvalue = trap_Cvar_VariableValue( "cg_chatBeep" ) != 0; s_preferences.teamchatbeep.curvalue = trap_Cvar_VariableValue( "cg_teamChatBeep" ) != 0; } static void Preferences_Event( void* ptr, int notification ) { if( notification != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_CROSSHAIR: s_preferences.crosshair.curvalue++; if( s_preferences.crosshair.curvalue == NUM_CROSSHAIRS || s_preferences.crosshairShader[s_preferences.crosshair.curvalue]==0 ) { s_preferences.crosshair.curvalue = 0; } trap_Cvar_SetValue( "cg_drawCrosshair", s_preferences.crosshair.curvalue ); break; case ID_CROSSHAIRHEALTH: trap_Cvar_SetValue( "cg_crosshairHealth", s_preferences.crosshairHealth.curvalue ); if(s_preferences.crosshairHealth.curvalue) { //If crosshairHealth is on: Don't allow color selection s_preferences.crosshairColorRed.generic.flags |= QMF_INACTIVE; s_preferences.crosshairColorGreen.generic.flags |= QMF_INACTIVE; s_preferences.crosshairColorBlue.generic.flags |= QMF_INACTIVE; } else { //If crosshairHealth is off: Allow color selection s_preferences.crosshairColorRed.generic.flags &= ~QMF_INACTIVE; s_preferences.crosshairColorGreen.generic.flags &= ~QMF_INACTIVE; s_preferences.crosshairColorBlue.generic.flags &= ~QMF_INACTIVE; } break; case ID_COLORRED: trap_Cvar_SetValue( "cg_crosshairColorRed", ((float)s_preferences.crosshairColorRed.curvalue)/255.f ); break; case ID_COLORGREEN: trap_Cvar_SetValue( "cg_crosshairColorGreen", ((float)s_preferences.crosshairColorGreen.curvalue)/255.f ); break; case ID_COLORBLUE: trap_Cvar_SetValue( "cg_crosshairColorBlue", ((float)s_preferences.crosshairColorBlue.curvalue)/255.f ); break; case ID_SIMPLEITEMS: trap_Cvar_SetValue( "cg_simpleItems", s_preferences.simpleitems.curvalue ); break; case ID_WEAPONBAR: trap_Cvar_SetValue( "cg_alwaysWeaponBar", s_preferences.alwaysweaponbar.curvalue ); break; case ID_HIGHQUALITYSKY: trap_Cvar_SetValue( "r_fastsky", !s_preferences.highqualitysky.curvalue ); break; case ID_EJECTINGBRASS: if ( s_preferences.brass.curvalue ) trap_Cvar_Reset( "cg_brassTime" ); else trap_Cvar_SetValue( "cg_brassTime", 0 ); break; case ID_WALLMARKS: trap_Cvar_SetValue( "cg_marks", s_preferences.wallmarks.curvalue ); break; case ID_DYNAMICLIGHTS: trap_Cvar_SetValue( "r_dynamiclight", s_preferences.dynamiclights.curvalue ); break; case ID_IDENTIFYTARGET: trap_Cvar_SetValue( "cg_drawCrosshairNames", s_preferences.identifytarget.curvalue ); break; case ID_SYNCEVERYFRAME: trap_Cvar_SetValue( "r_finish", s_preferences.synceveryframe.curvalue ); break; case ID_FORCEMODEL: trap_Cvar_SetValue( "cg_forcemodel", s_preferences.forcemodel.curvalue ); break; case ID_DRAWTEAMOVERLAY: trap_Cvar_SetValue( "cg_drawTeamOverlay", s_preferences.drawteamoverlay.curvalue ); break; case ID_ALLOWDOWNLOAD: trap_Cvar_SetValue( "cl_allowDownload", s_preferences.allowdownload.curvalue ); trap_Cvar_SetValue( "sv_allowDownload", s_preferences.allowdownload.curvalue ); break; case ID_DELAGHITSCAN: trap_Cvar_SetValue( "g_delagHitscan", s_preferences.delaghitscan.curvalue ); trap_Cvar_SetValue( "cg_delag", s_preferences.delaghitscan.curvalue ); break; case ID_CHATBEEP: trap_Cvar_SetValue( "cg_chatBeep", s_preferences.chatbeep.curvalue ); break; case ID_TEAMCHATBEEP: trap_Cvar_SetValue( "cg_teamChatBeep", s_preferences.teamchatbeep.curvalue ); break; case ID_BACK: UI_PopMenu(); break; } } /* ================= Crosshair_Draw ================= */ static void Crosshair_Draw( void *self ) { menulist_s *s; float *color; int x, y; int style; qboolean focus; vec4_t color4; s = (menulist_s *)self; x = s->generic.x; y = s->generic.y; style = UI_SMALLFONT; focus = (s->generic.parent->cursor == s->generic.menuPosition); if ( s->generic.flags & QMF_GRAYED ) color = text_color_disabled; else if ( focus ) { color = text_color_highlight; style |= UI_PULSE; } else if ( s->generic.flags & QMF_BLINK ) { color = text_color_highlight; style |= UI_BLINK; } else color = text_color_normal; if ( focus ) { // draw cursor UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color); } UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color ); if( !s->curvalue ) { return; } color4[0]=((float)s_preferences.crosshairColorRed.curvalue)/255.f; color4[1]=((float)s_preferences.crosshairColorGreen.curvalue)/255.f; color4[2]=((float)s_preferences.crosshairColorBlue.curvalue)/255.f; color4[3]=1.0f; trap_R_SetColor( color4 ); UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y - 4, 24, 24, s_preferences.crosshairShader[s->curvalue] ); } static void Preferences_MenuInit( void ) { int y; UI_SetDefaultCvar("cg_crosshairHealth","1"); UI_SetDefaultCvar("cg_crosshairColorRed","1"); UI_SetDefaultCvar("cg_crosshairColorBlue","1"); UI_SetDefaultCvar("cg_crosshairColorGreen","1"); memset( &s_preferences, 0 ,sizeof(preferences_t) ); Preferences_Cache(); s_preferences.menu.wrapAround = qtrue; s_preferences.menu.fullscreen = qtrue; s_preferences.banner.generic.type = MTYPE_BTEXT; s_preferences.banner.generic.x = 320; s_preferences.banner.generic.y = 16; s_preferences.banner.string = "GAME OPTIONS"; s_preferences.banner.color = color_white; s_preferences.banner.style = UI_CENTER; s_preferences.framel.generic.type = MTYPE_BITMAP; s_preferences.framel.generic.name = ART_FRAMEL; s_preferences.framel.generic.flags = QMF_INACTIVE; s_preferences.framel.generic.x = 0; s_preferences.framel.generic.y = 78; s_preferences.framel.width = 256; s_preferences.framel.height = 329; s_preferences.framer.generic.type = MTYPE_BITMAP; s_preferences.framer.generic.name = ART_FRAMER; s_preferences.framer.generic.flags = QMF_INACTIVE; s_preferences.framer.generic.x = 376; s_preferences.framer.generic.y = 76; s_preferences.framer.width = 256; s_preferences.framer.height = 334; y = 82; s_preferences.crosshair.generic.type = MTYPE_TEXT; s_preferences.crosshair.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT|QMF_NODEFAULTINIT|QMF_OWNERDRAW; s_preferences.crosshair.generic.x = PREFERENCES_X_POS; s_preferences.crosshair.generic.y = y; s_preferences.crosshair.generic.name = "Crosshair:"; s_preferences.crosshair.generic.callback = Preferences_Event; s_preferences.crosshair.generic.ownerdraw = Crosshair_Draw; s_preferences.crosshair.generic.id = ID_CROSSHAIR; s_preferences.crosshair.generic.top = y - 4; s_preferences.crosshair.generic.bottom = y + 20; s_preferences.crosshair.generic.left = PREFERENCES_X_POS - ( ( strlen(s_preferences.crosshair.generic.name) + 1 ) * SMALLCHAR_WIDTH ); s_preferences.crosshair.generic.right = PREFERENCES_X_POS + 48; y += BIGCHAR_HEIGHT+2; s_preferences.crosshairHealth.generic.type = MTYPE_RADIOBUTTON; s_preferences.crosshairHealth.generic.name = "Crosshair shows health:"; s_preferences.crosshairHealth.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.crosshairHealth.generic.callback = Preferences_Event; s_preferences.crosshairHealth.generic.id = ID_CROSSHAIRHEALTH; s_preferences.crosshairHealth.generic.x = PREFERENCES_X_POS; s_preferences.crosshairHealth.generic.y = y; y += BIGCHAR_HEIGHT; s_preferences.crosshairColorRed.generic.type = MTYPE_SLIDER; s_preferences.crosshairColorRed.generic.name = "Crosshair red:"; s_preferences.crosshairColorRed.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.crosshairColorRed.generic.callback = Preferences_Event; s_preferences.crosshairColorRed.generic.id = ID_COLORRED; s_preferences.crosshairColorRed.generic.x = PREFERENCES_X_POS; s_preferences.crosshairColorRed.generic.y = y; s_preferences.crosshairColorRed.minvalue = 0.0f; s_preferences.crosshairColorRed.maxvalue = 255.0f; y += BIGCHAR_HEIGHT+2; s_preferences.crosshairColorGreen.generic.type = MTYPE_SLIDER; s_preferences.crosshairColorGreen.generic.name = "Crosshair green:"; s_preferences.crosshairColorGreen.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.crosshairColorGreen.generic.callback = Preferences_Event; s_preferences.crosshairColorGreen.generic.id = ID_COLORGREEN; s_preferences.crosshairColorGreen.generic.x = PREFERENCES_X_POS; s_preferences.crosshairColorGreen.generic.y = y; s_preferences.crosshairColorGreen.minvalue = 0.0f; s_preferences.crosshairColorGreen.maxvalue = 255.0f; y += BIGCHAR_HEIGHT+2; s_preferences.crosshairColorBlue.generic.type = MTYPE_SLIDER; s_preferences.crosshairColorBlue.generic.name = "Crosshair blue:"; s_preferences.crosshairColorBlue.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.crosshairColorBlue.generic.callback = Preferences_Event; s_preferences.crosshairColorBlue.generic.id = ID_COLORBLUE; s_preferences.crosshairColorBlue.generic.x = PREFERENCES_X_POS; s_preferences.crosshairColorBlue.generic.y = y; s_preferences.crosshairColorBlue.minvalue = 0.0f; s_preferences.crosshairColorBlue.maxvalue = 255.0f; if(s_preferences.crosshairHealth.curvalue) { s_preferences.crosshairColorRed.generic.flags |= QMF_INACTIVE; s_preferences.crosshairColorGreen.generic.flags |= QMF_INACTIVE; s_preferences.crosshairColorBlue.generic.flags |= QMF_INACTIVE; } y += BIGCHAR_HEIGHT+2+4; s_preferences.simpleitems.generic.type = MTYPE_RADIOBUTTON; s_preferences.simpleitems.generic.name = "Simple Items:"; s_preferences.simpleitems.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.simpleitems.generic.callback = Preferences_Event; s_preferences.simpleitems.generic.id = ID_SIMPLEITEMS; s_preferences.simpleitems.generic.x = PREFERENCES_X_POS; s_preferences.simpleitems.generic.y = y; //Elimination y += BIGCHAR_HEIGHT; s_preferences.alwaysweaponbar.generic.type = MTYPE_RADIOBUTTON; s_preferences.alwaysweaponbar.generic.name = "Always show weapons:"; s_preferences.alwaysweaponbar.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.alwaysweaponbar.generic.callback = Preferences_Event; s_preferences.alwaysweaponbar.generic.id = ID_WEAPONBAR; s_preferences.alwaysweaponbar.generic.x = PREFERENCES_X_POS; s_preferences.alwaysweaponbar.generic.y = y; y += BIGCHAR_HEIGHT; s_preferences.wallmarks.generic.type = MTYPE_RADIOBUTTON; s_preferences.wallmarks.generic.name = "Marks on Walls:"; s_preferences.wallmarks.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.wallmarks.generic.callback = Preferences_Event; s_preferences.wallmarks.generic.id = ID_WALLMARKS; s_preferences.wallmarks.generic.x = PREFERENCES_X_POS; s_preferences.wallmarks.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.brass.generic.type = MTYPE_RADIOBUTTON; s_preferences.brass.generic.name = "Ejecting Brass:"; s_preferences.brass.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.brass.generic.callback = Preferences_Event; s_preferences.brass.generic.id = ID_EJECTINGBRASS; s_preferences.brass.generic.x = PREFERENCES_X_POS; s_preferences.brass.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.dynamiclights.generic.type = MTYPE_RADIOBUTTON; s_preferences.dynamiclights.generic.name = "Dynamic Lights:"; s_preferences.dynamiclights.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.dynamiclights.generic.callback = Preferences_Event; s_preferences.dynamiclights.generic.id = ID_DYNAMICLIGHTS; s_preferences.dynamiclights.generic.x = PREFERENCES_X_POS; s_preferences.dynamiclights.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.identifytarget.generic.type = MTYPE_RADIOBUTTON; s_preferences.identifytarget.generic.name = "Identify Target:"; s_preferences.identifytarget.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.identifytarget.generic.callback = Preferences_Event; s_preferences.identifytarget.generic.id = ID_IDENTIFYTARGET; s_preferences.identifytarget.generic.x = PREFERENCES_X_POS; s_preferences.identifytarget.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.highqualitysky.generic.type = MTYPE_RADIOBUTTON; s_preferences.highqualitysky.generic.name = "High Quality Sky:"; s_preferences.highqualitysky.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.highqualitysky.generic.callback = Preferences_Event; s_preferences.highqualitysky.generic.id = ID_HIGHQUALITYSKY; s_preferences.highqualitysky.generic.x = PREFERENCES_X_POS; s_preferences.highqualitysky.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.synceveryframe.generic.type = MTYPE_RADIOBUTTON; s_preferences.synceveryframe.generic.name = "Sync Every Frame:"; s_preferences.synceveryframe.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.synceveryframe.generic.callback = Preferences_Event; s_preferences.synceveryframe.generic.id = ID_SYNCEVERYFRAME; s_preferences.synceveryframe.generic.x = PREFERENCES_X_POS; s_preferences.synceveryframe.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.forcemodel.generic.type = MTYPE_RADIOBUTTON; s_preferences.forcemodel.generic.name = "Force Player Models:"; s_preferences.forcemodel.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.forcemodel.generic.callback = Preferences_Event; s_preferences.forcemodel.generic.id = ID_FORCEMODEL; s_preferences.forcemodel.generic.x = PREFERENCES_X_POS; s_preferences.forcemodel.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.drawteamoverlay.generic.type = MTYPE_SPINCONTROL; s_preferences.drawteamoverlay.generic.name = "Draw Team Overlay:"; s_preferences.drawteamoverlay.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.drawteamoverlay.generic.callback = Preferences_Event; s_preferences.drawteamoverlay.generic.id = ID_DRAWTEAMOVERLAY; s_preferences.drawteamoverlay.generic.x = PREFERENCES_X_POS; s_preferences.drawteamoverlay.generic.y = y; s_preferences.drawteamoverlay.itemnames = teamoverlay_names; y += BIGCHAR_HEIGHT+2; s_preferences.delaghitscan.generic.type = MTYPE_RADIOBUTTON; s_preferences.delaghitscan.generic.name = "Unlag hitscan:"; s_preferences.delaghitscan.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.delaghitscan.generic.callback = Preferences_Event; s_preferences.delaghitscan.generic.id = ID_DELAGHITSCAN; s_preferences.delaghitscan.generic.x = PREFERENCES_X_POS; s_preferences.delaghitscan.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.allowdownload.generic.type = MTYPE_RADIOBUTTON; s_preferences.allowdownload.generic.name = "Automatic Downloading:"; s_preferences.allowdownload.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.allowdownload.generic.callback = Preferences_Event; s_preferences.allowdownload.generic.id = ID_ALLOWDOWNLOAD; s_preferences.allowdownload.generic.x = PREFERENCES_X_POS; s_preferences.allowdownload.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.chatbeep.generic.type = MTYPE_RADIOBUTTON; s_preferences.chatbeep.generic.name = "Beep on chat:"; s_preferences.chatbeep.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.chatbeep.generic.callback = Preferences_Event; s_preferences.chatbeep.generic.id = ID_CHATBEEP; s_preferences.chatbeep.generic.x = PREFERENCES_X_POS; s_preferences.chatbeep.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.teamchatbeep.generic.type = MTYPE_RADIOBUTTON; s_preferences.teamchatbeep.generic.name = "Beep on team chat:"; s_preferences.teamchatbeep.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_preferences.teamchatbeep.generic.callback = Preferences_Event; s_preferences.teamchatbeep.generic.id = ID_TEAMCHATBEEP; s_preferences.teamchatbeep.generic.x = PREFERENCES_X_POS; s_preferences.teamchatbeep.generic.y = y; y += BIGCHAR_HEIGHT+2; s_preferences.back.generic.type = MTYPE_BITMAP; s_preferences.back.generic.name = ART_BACK0; s_preferences.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_preferences.back.generic.callback = Preferences_Event; s_preferences.back.generic.id = ID_BACK; s_preferences.back.generic.x = 0; s_preferences.back.generic.y = 480-64; s_preferences.back.width = 128; s_preferences.back.height = 64; s_preferences.back.focuspic = ART_BACK1; Menu_AddItem( &s_preferences.menu, &s_preferences.banner ); Menu_AddItem( &s_preferences.menu, &s_preferences.framel ); Menu_AddItem( &s_preferences.menu, &s_preferences.framer ); Menu_AddItem( &s_preferences.menu, &s_preferences.crosshair ); Menu_AddItem( &s_preferences.menu, &s_preferences.crosshairHealth ); Menu_AddItem( &s_preferences.menu, &s_preferences.crosshairColorRed ); Menu_AddItem( &s_preferences.menu, &s_preferences.crosshairColorGreen ); Menu_AddItem( &s_preferences.menu, &s_preferences.crosshairColorBlue ); Menu_AddItem( &s_preferences.menu, &s_preferences.simpleitems ); Menu_AddItem( &s_preferences.menu, &s_preferences.alwaysweaponbar ); Menu_AddItem( &s_preferences.menu, &s_preferences.wallmarks ); Menu_AddItem( &s_preferences.menu, &s_preferences.brass ); Menu_AddItem( &s_preferences.menu, &s_preferences.dynamiclights ); Menu_AddItem( &s_preferences.menu, &s_preferences.identifytarget ); Menu_AddItem( &s_preferences.menu, &s_preferences.highqualitysky ); Menu_AddItem( &s_preferences.menu, &s_preferences.synceveryframe ); Menu_AddItem( &s_preferences.menu, &s_preferences.forcemodel ); Menu_AddItem( &s_preferences.menu, &s_preferences.drawteamoverlay ); Menu_AddItem( &s_preferences.menu, &s_preferences.delaghitscan ); Menu_AddItem( &s_preferences.menu, &s_preferences.allowdownload ); Menu_AddItem( &s_preferences.menu, &s_preferences.teamchatbeep ); Menu_AddItem( &s_preferences.menu, &s_preferences.chatbeep ); Menu_AddItem( &s_preferences.menu, &s_preferences.back ); Preferences_SetMenuItems(); } /* =============== Preferences_Cache =============== */ void Preferences_Cache( void ) { int n; trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); for( n = 0; n < NUM_CROSSHAIRS; n++ ) { if (n < 10) s_preferences.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) ); else s_preferences.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%02d", n - 10) ); } } /* =============== UI_PreferencesMenu =============== */ void UI_PreferencesMenu( void ) { Preferences_MenuInit(); UI_PushMenu( &s_preferences.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_mfield.c0000644000175000017500000002102411656310261017255 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /* =================== MField_Draw Handles horizontal scrolling and cursor blinking x, y, are in pixels =================== */ void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ) { int len; int charw; int drawLen; int prestep; int cursorChar; char str[MAX_STRING_CHARS]; drawLen = edit->widthInChars; len = strlen( edit->buffer ) + 1; // guarantee that cursor will be visible if ( len <= drawLen ) { prestep = 0; } else { if ( edit->scroll + drawLen > len ) { edit->scroll = len - drawLen; if ( edit->scroll < 0 ) { edit->scroll = 0; } } prestep = edit->scroll; } if ( prestep + drawLen > len ) { drawLen = len - prestep; } // extract characters from the field at if ( drawLen >= MAX_STRING_CHARS ) { trap_Error( "drawLen >= MAX_STRING_CHARS" ); } memcpy( str, edit->buffer + prestep, drawLen ); str[ drawLen ] = 0; UI_DrawString( x, y, str, style, color ); // draw the cursor if (!(style & UI_PULSE)) { return; } if ( trap_Key_GetOverstrikeMode() ) { cursorChar = 11; } else { cursorChar = 10; } style &= ~UI_PULSE; style |= UI_BLINK; if (style & UI_SMALLFONT) { charw = SMALLCHAR_WIDTH; } else if (style & UI_GIANTFONT) { charw = GIANTCHAR_WIDTH; } else { charw = BIGCHAR_WIDTH; } if (style & UI_CENTER) { len = strlen(str); x = x - len*charw/2; } else if (style & UI_RIGHT) { len = strlen(str); x = x - len*charw; } UI_DrawChar( x + ( edit->cursor - prestep ) * charw, y, cursorChar, style & ~(UI_CENTER|UI_RIGHT), color ); } /* ================ MField_Paste ================ */ void MField_Paste( mfield_t *edit ) { char pasteBuffer[64]; int pasteLen, i; trap_GetClipboardData( pasteBuffer, 64 ); // send as if typed, so insert / overstrike works properly pasteLen = strlen( pasteBuffer ); for ( i = 0 ; i < pasteLen ; i++ ) { MField_CharEvent( edit, pasteBuffer[i] ); } } /* ================= MField_KeyDownEvent Performs the basic line editing functions for the console, in-game talk, and menu fields Key events are used for non-printable characters, others are gotten from char events. ================= */ void MField_KeyDownEvent( mfield_t *edit, int key ) { int len; // shift-insert is paste if ( ( key == K_INS ) && trap_Key_IsDown( K_SHIFT ) ) { MField_Paste( edit ); return; } len = strlen( edit->buffer ); if ( key == K_DEL ) { if ( edit->cursor < len ) { memmove( edit->buffer + edit->cursor, edit->buffer + edit->cursor + 1, len - edit->cursor ); } return; } if ( key == K_RIGHTARROW ) { if ( edit->cursor < len ) { edit->cursor++; } if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) { edit->scroll++; } return; } if ( key == K_LEFTARROW ) { if ( edit->cursor > 0 ) { edit->cursor--; } if ( edit->cursor < edit->scroll ) { edit->scroll--; } return; } if ( key == K_HOME || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) { edit->cursor = 0; edit->scroll = 0; return; } if ( key == K_END || ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) { edit->cursor = len; edit->scroll = len - edit->widthInChars + 1; if (edit->scroll < 0) edit->scroll = 0; return; } if ( key == K_INS ) { trap_Key_SetOverstrikeMode( !trap_Key_GetOverstrikeMode() ); return; } } /* ================== MField_CharEvent ================== */ void MField_CharEvent( mfield_t *edit, int ch ) { int len; if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste MField_Paste( edit ); return; } if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field MField_Clear( edit ); return; } len = strlen( edit->buffer ); if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace if ( edit->cursor > 0 ) { memmove( edit->buffer + edit->cursor - 1, edit->buffer + edit->cursor, len + 1 - edit->cursor ); edit->cursor--; if ( edit->cursor < edit->scroll ) { edit->scroll--; } } return; } if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home edit->cursor = 0; edit->scroll = 0; return; } if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end edit->cursor = len; edit->scroll = edit->cursor - edit->widthInChars + 1; if (edit->scroll < 0) edit->scroll = 0; return; } // // ignore any other non printable chars // if ( ch < 32 ) { return; } if ( !trap_Key_GetOverstrikeMode() ) { if ((edit->cursor == MAX_EDIT_LINE - 1) || (edit->maxchars && edit->cursor >= edit->maxchars)) return; } else { // insert mode if (( len == MAX_EDIT_LINE - 1 ) || (edit->maxchars && len >= edit->maxchars)) return; memmove( edit->buffer + edit->cursor + 1, edit->buffer + edit->cursor, len + 1 - edit->cursor ); } edit->buffer[edit->cursor] = ch; if (!edit->maxchars || edit->cursor < edit->maxchars-1) edit->cursor++; if ( edit->cursor >= edit->widthInChars ) { edit->scroll++; } if ( edit->cursor == len + 1) { edit->buffer[edit->cursor] = 0; } } /* ================== MField_Clear ================== */ void MField_Clear( mfield_t *edit ) { edit->buffer[0] = 0; edit->cursor = 0; edit->scroll = 0; } /* ================== MenuField_Init ================== */ void MenuField_Init( menufield_s* m ) { int l; int w; int h; MField_Clear( &m->field ); if (m->generic.flags & QMF_SMALLFONT) { w = SMALLCHAR_WIDTH; h = SMALLCHAR_HEIGHT; } else { w = BIGCHAR_WIDTH; h = BIGCHAR_HEIGHT; } if (m->generic.name) { l = (strlen( m->generic.name )+1) * w; } else { l = 0; } m->generic.left = m->generic.x - l; m->generic.top = m->generic.y; m->generic.right = m->generic.x + w + m->field.widthInChars*w; m->generic.bottom = m->generic.y + h; } /* ================== MenuField_Draw ================== */ void MenuField_Draw( menufield_s *f ) { int x; int y; int w; int h; int style; qboolean focus; float *color; x = f->generic.x; y = f->generic.y; if (f->generic.flags & QMF_SMALLFONT) { w = SMALLCHAR_WIDTH; h = SMALLCHAR_HEIGHT; style = UI_SMALLFONT; } else { w = BIGCHAR_WIDTH; h = BIGCHAR_HEIGHT; style = UI_BIGFONT; } if (Menu_ItemAtCursor( f->generic.parent ) == f) { focus = qtrue; style |= UI_PULSE; } else { focus = qfalse; } if (f->generic.flags & QMF_GRAYED) color = text_color_disabled; else if (focus) color = text_color_highlight; else color = text_color_normal; if ( focus ) { // draw cursor UI_FillRect( f->generic.left, f->generic.top, f->generic.right-f->generic.left+1, f->generic.bottom-f->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|style, color); } if ( f->generic.name ) { UI_DrawString( x - w, y, f->generic.name, style|UI_RIGHT, color ); } MField_Draw( &f->field, x + w, y, style, color ); } /* ================== MenuField_Key ================== */ sfxHandle_t MenuField_Key( menufield_s* m, int* key ) { int keycode; keycode = *key; switch ( keycode ) { case K_KP_ENTER: case K_ENTER: case K_JOY1: case K_JOY2: case K_JOY3: case K_JOY4: // have enter go to next cursor point *key = K_TAB; break; case K_TAB: case K_DOWNARROW: case K_UPARROW: break; default: if ( keycode & K_CHAR_FLAG ) { keycode &= ~K_CHAR_FLAG; if ((m->generic.flags & QMF_UPPERCASE) && Q_islower( keycode )) keycode -= 'a' - 'A'; else if ((m->generic.flags & QMF_LOWERCASE) && Q_isupper( keycode )) keycode -= 'A' - 'a'; else if ((m->generic.flags & QMF_NUMBERSONLY) && Q_isalpha( keycode )) return (menu_buzz_sound); MField_CharEvent( &m->field, keycode); } else MField_KeyDownEvent( &m->field, keycode ); break; } return (0); } openarena_0.8.8.orig/code/q3_ui/ui_startserver.c0000644000175000017500000022504011656310261020405 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= START SERVER MENU ***** ============================================================================= */ #include "ui_local.h" #define GAMESERVER_BACK0 "menu/art_blueish/back_0" #define GAMESERVER_BACK1 "menu/art_blueish/back_1" #define GAMESERVER_NEXT0 "menu/art_blueish/next_0" #define GAMESERVER_NEXT1 "menu/art_blueish/next_1" #define GAMESERVER_FRAMEL "menu/art_blueish/frame2_l" #define GAMESERVER_FRAMER "menu/art_blueish/frame1_r" #define GAMESERVER_SELECT "menu/art_blueish/maps_select" #define GAMESERVER_SELECTED "menu/art_blueish/maps_selected" #define GAMESERVER_FIGHT0 "menu/art_blueish/fight_0" #define GAMESERVER_FIGHT1 "menu/art_blueish/fight_1" #define GAMESERVER_UNKNOWNMAP "menu/art/unknownmap" #define GAMESERVER_ARROWS "menu/art_blueish/gs_arrows_0" #define GAMESERVER_ARROWSL "menu/art_blueish/gs_arrows_l" #define GAMESERVER_ARROWSR "menu/art_blueish/gs_arrows_r" #define MAX_MAPROWS 4 #define MAX_MAPCOLS 2 #define MAX_MAPSPERPAGE (MAX_MAPROWS * MAX_MAPCOLS) //#define MAX_SERVERSTEXT 8192 #define MAX_SERVERMAPS MAX_ARENAS #define MAX_NAMELENGTH 16 #define ID_GAMETYPE 10 #define ID_PICTURES 11 // 12, 13, 14, 15, 16, 17, 18 #define ID_PREVPAGE 19 #define ID_NEXTPAGE 20 #define ID_STARTSERVERBACK 21 #define ID_STARTSERVERNEXT 22 #define ID_AUTONEXTMAP 23 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menulist_s gametype; menuradiobutton_s autonextmap; menubitmap_s mappics[MAX_MAPSPERPAGE]; menubitmap_s mapbuttons[MAX_MAPSPERPAGE]; menubitmap_s arrows; menubitmap_s prevpage; menubitmap_s nextpage; menubitmap_s back; menubitmap_s next; menutext_s mapname; menubitmap_s item_null; qboolean multiplayer; int currentmap; int nummaps; int page; int maxpages; int maplist[MAX_SERVERMAPS]; } startserver_t; static startserver_t s_startserver; static const char *gametype_items[] = { "Free For All", "Team Deathmatch", "Tournament", "Capture the Flag", "One Flag Capture", "Overload", "Harvester", "Elimination", "CTF Elimination", "Last Man Standing", "Double Domination", "Domination", NULL }; static int gametype_remap[] = { GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF, GT_1FCTF, GT_OBELISK, GT_HARVESTER, GT_ELIMINATION, GT_CTF_ELIMINATION, GT_LMS, GT_DOUBLE_D, GT_DOMINATION }; static int gametype_remap2[] = { 0, 2, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; //this works and should increment for more gametypes static void UI_ServerOptionsMenu( qboolean multiplayer ); /* ================= GametypeBits from arenas.txt + .arena files ================= */ static int GametypeBits( char *string ) { int bits; char *p; char *token; bits = 0; p = string; while( 1 ) { token = COM_ParseExt( &p, qfalse ); if( token[0] == 0 ) { break; } if( Q_stricmp( token, "ffa" ) == 0 ) { bits |= 1 << GT_FFA; continue; } if( Q_stricmp( token, "tourney" ) == 0 ) { bits |= 1 << GT_TOURNAMENT; continue; } if( Q_stricmp( token, "single" ) == 0 ) { bits |= 1 << GT_SINGLE_PLAYER; continue; } if( Q_stricmp( token, "team" ) == 0 ) { bits |= 1 << GT_TEAM; continue; } if( Q_stricmp( token, "ctf" ) == 0 ) { bits |= 1 << GT_CTF; continue; } if( Q_stricmp( token, "oneflag" ) == 0 ) { bits |= 1 << GT_1FCTF; continue; } if( Q_stricmp( token, "overload" ) == 0 ) { bits |= 1 << GT_OBELISK; continue; } if( Q_stricmp( token, "harvester" ) == 0 ) { bits |= 1 << GT_HARVESTER; continue; } if( Q_stricmp( token, "elimination" ) == 0 ) { bits |= 1 << GT_ELIMINATION; continue; } if( Q_stricmp( token, "ctfelimination" ) == 0 ) { bits |= 1 << GT_CTF_ELIMINATION; continue; } if( Q_stricmp( token, "lms" ) == 0 ) { bits |= 1 << GT_LMS; continue; } if( Q_stricmp( token, "dd" ) == 0 ) { bits |= 1 << GT_DOUBLE_D; continue; } if( Q_stricmp( token, "dom" ) == 0 ) { bits |= 1 << GT_DOMINATION; continue; } } return bits; } /* ================= StartServer_Update ================= */ static void StartServer_Update( void ) { int i; int top; static char picname[MAX_MAPSPERPAGE][64]; const char *info; char mapname[MAX_NAMELENGTH]; top = s_startserver.page*MAX_MAPSPERPAGE; for (i=0; i= s_startserver.nummaps) break; info = UI_GetArenaInfoByNumber( s_startserver.maplist[ top + i ]); Q_strncpyz( mapname, Info_ValueForKey( info, "map"), MAX_NAMELENGTH ); Q_strupr( mapname ); Com_sprintf( picname[i], sizeof(picname[i]), "levelshots/%s", mapname ); s_startserver.mappics[i].generic.flags &= ~((unsigned int)QMF_HIGHLIGHT); s_startserver.mappics[i].generic.name = picname[i]; s_startserver.mappics[i].shader = 0; // reset s_startserver.mapbuttons[i].generic.flags |= QMF_PULSEIFFOCUS; s_startserver.mapbuttons[i].generic.flags &= ~((unsigned int)QMF_INACTIVE); } for (; i=0 && i < MAX_MAPSPERPAGE ) { s_startserver.mappics[i].generic.flags |= QMF_HIGHLIGHT; s_startserver.mapbuttons[i].generic.flags &= ~((unsigned int)QMF_PULSEIFFOCUS); } // set the map name info = UI_GetArenaInfoByNumber( s_startserver.maplist[ s_startserver.currentmap ]); Q_strncpyz( s_startserver.mapname.string, Info_ValueForKey( info, "map" ), MAX_NAMELENGTH); } Q_strupr( s_startserver.mapname.string ); } /* ================= StartServer_MapEvent ================= */ static void StartServer_MapEvent( void* ptr, int event ) { if( event != QM_ACTIVATED) { return; } s_startserver.currentmap = (s_startserver.page*MAX_MAPSPERPAGE) + (((menucommon_s*)ptr)->id - ID_PICTURES); StartServer_Update(); } /* ================= StartServer_GametypeEvent ================= */ static void StartServer_GametypeEvent( void* ptr, int event ) { int i; int count; int gamebits; int matchbits; const char *info; if( event != QM_ACTIVATED) { return; } count = UI_GetNumArenas(); s_startserver.nummaps = 0; matchbits = 1 << gametype_remap[s_startserver.gametype.curvalue]; if( gametype_remap[s_startserver.gametype.curvalue] == GT_FFA ) { matchbits |= ( 1 << GT_SINGLE_PLAYER ); } for( i = 0; i < count; i++ ) { info = UI_GetArenaInfoByNumber( i ); gamebits = GametypeBits( Info_ValueForKey( info, "type") ); if( !( gamebits & matchbits ) ) { continue; } s_startserver.maplist[s_startserver.nummaps] = i; s_startserver.nummaps++; } s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE; s_startserver.page = 0; s_startserver.currentmap = 0; StartServer_Update(); } /* ================= StartServer_MenuEvent ================= */ static void StartServer_MenuEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_PREVPAGE: if( s_startserver.page > 0 ) { s_startserver.page--; StartServer_Update(); } break; case ID_NEXTPAGE: if( s_startserver.page < s_startserver.maxpages - 1 ) { s_startserver.page++; StartServer_Update(); } break; case ID_STARTSERVERNEXT: trap_Cvar_SetValue( "g_gameType", gametype_remap[s_startserver.gametype.curvalue] ); UI_ServerOptionsMenu( s_startserver.multiplayer ); break; case ID_AUTONEXTMAP: //trap_Cvar_SetValue( "cg_alwaysWeaponBar", s_preferences.alwaysweaponbar.curvalue ); trap_Cvar_SetValue( "g_autonextmap", s_startserver.autonextmap.curvalue ); break; case ID_STARTSERVERBACK: UI_PopMenu(); break; } } /* =============== StartServer_LevelshotDraw =============== */ static void StartServer_LevelshotDraw( void *self ) { menubitmap_s *b; int x; int y; int w; int h; int n; const char *info; b = (menubitmap_s *)self; if( !b->generic.name ) { return; } if( b->generic.name && !b->shader ) { b->shader = trap_R_RegisterShaderNoMip( b->generic.name ); if( !b->shader && b->errorpic ) { b->shader = trap_R_RegisterShaderNoMip( b->errorpic ); } } if( b->focuspic && !b->focusshader ) { b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic ); } x = b->generic.x; y = b->generic.y; w = b->width; h = b->height; if( b->shader ) { UI_DrawHandlePic( x, y, w, h, b->shader ); } x = b->generic.x; y = b->generic.y + b->height; UI_FillRect( x, y, b->width, 28, colorBlack ); x += b->width / 2; y += 4; n = s_startserver.page * MAX_MAPSPERPAGE + b->generic.id - ID_PICTURES; info = UI_GetArenaInfoByNumber( s_startserver.maplist[ n ]); UI_DrawString( x, y, Info_ValueForKey( info, "map" ), UI_CENTER|UI_SMALLFONT, color_orange ); x = b->generic.x; y = b->generic.y; w = b->width; h = b->height + 28; if( b->generic.flags & QMF_HIGHLIGHT ) { UI_DrawHandlePic( x, y, w, h, b->focusshader ); } } /* ================= StartServer_MenuInit ================= */ static void StartServer_MenuInit( void ) { int i; int x; int y; static char mapnamebuffer[64]; // zero set all our globals memset( &s_startserver, 0 ,sizeof(startserver_t) ); StartServer_Cache(); s_startserver.autonextmap.curvalue = trap_Cvar_VariableValue( "g_autonextmap" ) != 0; s_startserver.menu.wrapAround = qtrue; s_startserver.menu.fullscreen = qtrue; s_startserver.banner.generic.type = MTYPE_BTEXT; s_startserver.banner.generic.x = 320; s_startserver.banner.generic.y = 16; s_startserver.banner.string = "GAME SERVER"; s_startserver.banner.color = color_white; s_startserver.banner.style = UI_CENTER; s_startserver.framel.generic.type = MTYPE_BITMAP; s_startserver.framel.generic.name = GAMESERVER_FRAMEL; s_startserver.framel.generic.flags = QMF_INACTIVE; s_startserver.framel.generic.x = 0; s_startserver.framel.generic.y = 78; s_startserver.framel.width = 256; s_startserver.framel.height = 329; s_startserver.framer.generic.type = MTYPE_BITMAP; s_startserver.framer.generic.name = GAMESERVER_FRAMER; s_startserver.framer.generic.flags = QMF_INACTIVE; s_startserver.framer.generic.x = 376; s_startserver.framer.generic.y = 76; s_startserver.framer.width = 256; s_startserver.framer.height = 334; s_startserver.autonextmap.generic.type = MTYPE_RADIOBUTTON; s_startserver.autonextmap.generic.name = "Auto change map:"; s_startserver.autonextmap.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_startserver.autonextmap.generic.callback = StartServer_MenuEvent; s_startserver.autonextmap.generic.id = ID_AUTONEXTMAP; s_startserver.autonextmap.generic.x = 320 +24; s_startserver.autonextmap.generic.y = 368; s_startserver.gametype.generic.type = MTYPE_SPINCONTROL; s_startserver.gametype.generic.name = "Game Type:"; s_startserver.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_startserver.gametype.generic.callback = StartServer_GametypeEvent; s_startserver.gametype.generic.id = ID_GAMETYPE; s_startserver.gametype.generic.x = 320 - 24; s_startserver.gametype.generic.y = 70; s_startserver.gametype.itemnames = gametype_items; for (i=0; i= GT_TEAM) && s_serveroptions.gametype != GT_LMS && (s_serveroptions.playerTeam[n].curvalue != s_serveroptions.playerTeam[s_serveroptions.newBotIndex].curvalue ) ) { continue; } if( Q_stricmp( checkName, s_serveroptions.playerNameBuffers[n] ) == 0 ) { return qtrue; } } return qfalse; } /* ================= ServerOptions_Start ================= */ static void ServerOptions_Start( void ) { int timelimit; int fraglimit; int maxclients; // int dedicated; int friendlyfire; int flaglimit; int pure; int pmove; int lan; int instantgib; int rockets; int oneway; int lmsMode; int skill; int n; const char *info; char buf[64]; timelimit = atoi( s_serveroptions.timelimit.field.buffer ); fraglimit = atoi( s_serveroptions.fraglimit.field.buffer ); flaglimit = atoi( s_serveroptions.flaglimit.field.buffer ); // dedicated = s_serveroptions.dedicated.curvalue; friendlyfire = s_serveroptions.friendlyfire.curvalue; pure = s_serveroptions.pure.curvalue; lan = s_serveroptions.lan.curvalue; pmove = s_serveroptions.pmove.curvalue; instantgib = s_serveroptions.instantgib.curvalue; rockets = s_serveroptions.rockets.curvalue; oneway = s_serveroptions.oneway.curvalue; //Sago: For some reason you need to add 1 to curvalue to get the UI to show the right thing (fixed?) lmsMode = s_serveroptions.lmsMode.curvalue; //+1; skill = s_serveroptions.botSkill.curvalue + 1; //set maxclients for( n = 0, maxclients = 0; n < PLAYER_SLOTS; n++ ) { if( s_serveroptions.playerType[n].curvalue == 2 ) { continue; } if( (s_serveroptions.playerType[n].curvalue == 1) && (s_serveroptions.playerNameBuffers[n][0] == 0) ) { continue; } maxclients++; } switch( s_serveroptions.gametype ) { case GT_FFA: default: trap_Cvar_SetValue( "ui_ffa_fraglimit", fraglimit ); trap_Cvar_SetValue( "ui_ffa_timelimit", timelimit ); break; case GT_TOURNAMENT: trap_Cvar_SetValue( "ui_tourney_fraglimit", fraglimit ); trap_Cvar_SetValue( "ui_tourney_timelimit", timelimit ); break; case GT_TEAM: trap_Cvar_SetValue( "ui_team_fraglimit", fraglimit ); trap_Cvar_SetValue( "ui_team_timelimit", timelimit ); trap_Cvar_SetValue( "ui_team_friendlt", friendlyfire ); break; case GT_CTF: trap_Cvar_SetValue( "ui_ctf_fraglimit", fraglimit ); trap_Cvar_SetValue( "ui_ctf_timelimit", timelimit ); trap_Cvar_SetValue( "ui_ctf_friendlt", friendlyfire ); break; case GT_1FCTF: trap_Cvar_SetValue( "ui_1fctf_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_1fctf_timelimit", timelimit ); trap_Cvar_SetValue( "ui_1fctf_friendlt", friendlyfire ); break; case GT_OBELISK: trap_Cvar_SetValue( "ui_overload_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_overload_timelimit", timelimit ); trap_Cvar_SetValue( "ui_overload_friendlt", friendlyfire ); break; case GT_HARVESTER: trap_Cvar_SetValue( "ui_harvester_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_harvester_timelimit", timelimit ); trap_Cvar_SetValue( "ui_harvester_friendlt", friendlyfire ); break; case GT_ELIMINATION: trap_Cvar_SetValue( "ui_elimination_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_elimination_timelimit", timelimit ); //trap_Cvar_SetValue( "ui_elimination_friendlt", friendlyfire ); break; case GT_CTF_ELIMINATION: trap_Cvar_SetValue( "ui_ctf_elimination_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_ctf_elimination_timelimit", timelimit ); //trap_Cvar_SetValue( "ui_ctf_elimination_friendlt", friendlyfire ); break; case GT_LMS: trap_Cvar_SetValue( "ui_lms_fraglimit", fraglimit ); trap_Cvar_SetValue( "ui_lms_timelimit", timelimit ); break; case GT_DOUBLE_D: trap_Cvar_SetValue( "ui_dd_capturelimit", fraglimit ); trap_Cvar_SetValue( "ui_dd_timelimit", timelimit ); trap_Cvar_SetValue( "ui_dd_friendlt", friendlyfire ); break; } trap_Cvar_SetValue( "sv_maxclients", Com_Clamp( 0, 12, maxclients ) ); // trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, dedicated ) ); trap_Cvar_SetValue ("timelimit", Com_Clamp( 0, timelimit, timelimit ) ); trap_Cvar_SetValue ("fraglimit", Com_Clamp( 0, fraglimit, fraglimit ) ); trap_Cvar_SetValue ("capturelimit", Com_Clamp( 0, flaglimit, flaglimit ) ); trap_Cvar_SetValue( "g_friendlyfire", friendlyfire ); trap_Cvar_SetValue( "sv_pure", pure ); trap_Cvar_SetValue( "sv_lanForceRate", lan ); trap_Cvar_SetValue( "g_instantgib", instantgib ); trap_Cvar_SetValue( "g_rockets", rockets ); trap_Cvar_SetValue( "g_lms_mode", lmsMode); trap_Cvar_SetValue( "elimination_ctf_oneway", oneway ); switch(pmove) { case 1: //Fixed framerate 125 Hz trap_Cvar_SetValue( "pmove_fixed", 1); trap_Cvar_SetValue( "pmove_msec", 8); trap_Cvar_SetValue( "pmove_float", 0); break; case 2: //Fixed framerate 91 Hz trap_Cvar_SetValue( "pmove_fixed", 1); trap_Cvar_SetValue( "pmove_msec", 11); trap_Cvar_SetValue( "pmove_float", 0); break; case 3: //Accurate physics trap_Cvar_SetValue( "pmove_fixed", 0); trap_Cvar_SetValue( "pmove_float", 1); break; default: //Framerate dependent trap_Cvar_SetValue( "pmove_fixed", 0); trap_Cvar_SetValue( "pmove_float", 0); break; }; trap_Cvar_Set("sv_hostname", s_serveroptions.hostname.field.buffer ); // the wait commands will allow the dedicated to take effect info = UI_GetArenaInfoByNumber( s_startserver.maplist[ s_startserver.currentmap ]); trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", Info_ValueForKey( info, "map" ))); // add bots trap_Cmd_ExecuteText( EXEC_APPEND, "wait 3\n" ); for( n = 1; n < PLAYER_SLOTS; n++ ) { if( s_serveroptions.playerType[n].curvalue != 1 ) { continue; } if( s_serveroptions.playerNameBuffers[n][0] == 0 ) { continue; } if( s_serveroptions.playerNameBuffers[n][0] == '-' ) { continue; } if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS ) { Com_sprintf( buf, sizeof(buf), "addbot %s %i %s\n", s_serveroptions.playerNameBuffers[n], skill, playerTeam_list[s_serveroptions.playerTeam[n].curvalue] ); } else { Com_sprintf( buf, sizeof(buf), "addbot %s %i\n", s_serveroptions.playerNameBuffers[n], skill ); } trap_Cmd_ExecuteText( EXEC_APPEND, buf ); } // set player's team if( /*dedicated == 0 &&*/ s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS ) { trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait 5; team %s\n", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ) ); } } /* ================= ServerOptions_InitPlayerItems ================= */ static void ServerOptions_InitPlayerItems( void ) { int n; int v; // init types if( s_serveroptions.multiplayer ) { v = 0; // open } else { v = 1; // bot } for( n = 0; n < PLAYER_SLOTS; n++ ) { s_serveroptions.playerType[n].curvalue = v; } if( s_serveroptions.multiplayer && (s_serveroptions.gametype < GT_TEAM || s_serveroptions.gametype == GT_LMS ) ) { for( n = 8; n < PLAYER_SLOTS; n++ ) { s_serveroptions.playerType[n].curvalue = 2; } } // if not a dedicated server, first slot is reserved for the human on the server // if( s_serveroptions.dedicated.curvalue == 0 ) { // human s_serveroptions.playerType[0].generic.flags |= QMF_INACTIVE; s_serveroptions.playerType[0].curvalue = 0; trap_Cvar_VariableStringBuffer( "name", s_serveroptions.playerNameBuffers[0], sizeof(s_serveroptions.playerNameBuffers[0]) ); Q_CleanStr( s_serveroptions.playerNameBuffers[0] ); // } // init teams if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS ) { for( n = 0; n < (PLAYER_SLOTS / 2); n++ ) { s_serveroptions.playerTeam[n].curvalue = 0; } for( ; n < PLAYER_SLOTS; n++ ) { s_serveroptions.playerTeam[n].curvalue = 1; } } else { for( n = 0; n < PLAYER_SLOTS; n++ ) { s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); } } } /* ================= ServerOptions_SetPlayerItems ================= */ static void ServerOptions_SetPlayerItems( void ) { int start; int n; // types // for( n = 0; n < PLAYER_SLOTS; n++ ) { // if( (!s_serveroptions.multiplayer) && (n > 0) && (s_serveroptions.playerType[n].curvalue == 0) ) { // s_serveroptions.playerType[n].curvalue = 1; // } // } // names // if( s_serveroptions.dedicated.curvalue == 0 ) { s_serveroptions.player0.string = "Human"; s_serveroptions.playerName[0].generic.flags &= ~((unsigned int)QMF_HIDDEN); start = 1; /* } else { s_serveroptions.player0.string = "Open"; start = 0; }*/ for( n = start; n < PLAYER_SLOTS; n++ ) { if( s_serveroptions.playerType[n].curvalue == 1 ) { s_serveroptions.playerName[n].generic.flags &= ~( (unsigned int)(QMF_INACTIVE|QMF_HIDDEN)); } else { s_serveroptions.playerName[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); } } // teams if( s_serveroptions.gametype < GT_TEAM || s_serveroptions.gametype == GT_LMS ) { return; } for( n = start; n < PLAYER_SLOTS; n++ ) { if( s_serveroptions.playerType[n].curvalue == 2 ) { s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); } else { s_serveroptions.playerTeam[n].generic.flags &= ~((unsigned int) (QMF_INACTIVE|QMF_HIDDEN)); } } } /* ================= ServerOptions_Event ================= */ static void ServerOptions_Event( void* ptr, int event ) { switch( ((menucommon_s*)ptr)->id ) { //if( event != QM_ACTIVATED && event != QM_LOSTFOCUS) { // return; //} case ID_PLAYER_TYPE: if( event != QM_ACTIVATED ) { break; } ServerOptions_SetPlayerItems(); break; case ID_MAXCLIENTS: // case ID_DEDICATED: ServerOptions_SetPlayerItems(); break; case ID_GO: if( event != QM_ACTIVATED ) { break; } ServerOptions_Start(); break; case ID_STARTSERVERNEXT: if( event != QM_ACTIVATED ) { break; } break; case ID_BACK: if( event != QM_ACTIVATED ) { break; } UI_PopMenu(); break; } } static void ServerOptions_PlayerNameEvent( void* ptr, int event ) { int n; if( event != QM_ACTIVATED ) { return; } n = ((menutext_s*)ptr)->generic.id; s_serveroptions.newBotIndex = n; UI_BotSelectMenu( s_serveroptions.playerNameBuffers[n] ); } /* ================= ServerOptions_StatusBar ================= */ static void ServerOptions_StatusBar( void* ptr ) { UI_DrawString( 320, 440, "0 = NO LIMIT", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= ServerOptions_StatusBar_Instantgib ================= */ static void ServerOptions_StatusBar_Instantgib( void* ptr ) { UI_DrawString( 320, 440, "Only railgun and instant kill", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= ServerOptions_StatusBar_Allrockets ================= */ static void ServerOptions_StatusBar_Allrockets( void* ptr ) { UI_DrawString( 320, 440, "Only Rocket launcher with Inf. ammo", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= ServerOptions_StatusBar_Pure ================= */ static void ServerOptions_StatusBar_Pure( void* ptr ) { UI_DrawString( 320, 440, "Require identical pk3 files", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= ServerOptions_StatusBar_Oneway ================= */ static void ServerOptions_StatusBar_Oneway( void* ptr ) { UI_DrawString( 320, 440, "Only one team can capture in a round", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= ServerOptions_StatusBar_Pmove ================= */ static void ServerOptions_StatusBar_Pmove( void* ptr ) { switch( ((menulist_s*)ptr)->curvalue ) { case 0: UI_DrawString( 320, 440, "Physics depends on players framerates", UI_CENTER|UI_SMALLFONT, colorWhite ); UI_DrawString( 320, 460, "Not all players are equal", UI_CENTER|UI_SMALLFONT, colorWhite ); break; case 1: case 2: UI_DrawString( 320, 440, "Physics are calculated at fixed intervals", UI_CENTER|UI_SMALLFONT, colorWhite ); UI_DrawString( 320, 460, "All players are equal", UI_CENTER|UI_SMALLFONT, colorWhite ); break; case 3: UI_DrawString( 320, 440, "Physics are calculated exactly", UI_CENTER|UI_SMALLFONT, colorWhite ); UI_DrawString( 320, 460, "All players are equal", UI_CENTER|UI_SMALLFONT, colorWhite ); break; default: UI_DrawString( 320, 440, "Framerate dependent or not", UI_CENTER|UI_SMALLFONT, colorWhite ); break; } } /* =============== ServerOptions_LevelshotDraw =============== */ static void ServerOptions_LevelshotDraw( void *self ) { menubitmap_s *b; int x; int y; // strange place for this, but it works if( s_serveroptions.newBot ) { Q_strncpyz( s_serveroptions.playerNameBuffers[s_serveroptions.newBotIndex], s_serveroptions.newBotName, 16 ); s_serveroptions.newBot = qfalse; } b = (menubitmap_s *)self; Bitmap_Draw( b ); x = b->generic.x; y = b->generic.y + b->height; UI_FillRect( x, y, b->width, 40, colorBlack ); x += b->width / 2; y += 4; UI_DrawString( x, y, s_serveroptions.mapnamebuffer, UI_CENTER|UI_SMALLFONT, color_orange ); y += SMALLCHAR_HEIGHT; UI_DrawString( x, y, gametype_items[gametype_remap2[s_serveroptions.gametype]], UI_CENTER|UI_SMALLFONT, color_orange ); } static void ServerOptions_InitBotNames( void ) { int count; int n; const char *arenaInfo; const char *botInfo; char *p; char *bot; char bots[MAX_INFO_STRING]; //this SHOULD work if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS ) { Q_strncpyz( s_serveroptions.playerNameBuffers[1], "gargoyle", 16 ); Q_strncpyz( s_serveroptions.playerNameBuffers[2], "kyonshi", 16 ); Q_strncpyz( s_serveroptions.playerNameBuffers[3], "grism", 16 ); if( s_serveroptions.gametype != GT_TEAM ) { s_serveroptions.playerType[3].curvalue = 2; } Q_strncpyz( s_serveroptions.playerNameBuffers[4], "merman", 16 ); s_serveroptions.playerType[4].curvalue = 2; Q_strncpyz( s_serveroptions.playerNameBuffers[5], "skelebot", 16 ); s_serveroptions.playerType[5].curvalue = 2; Q_strncpyz( s_serveroptions.playerNameBuffers[6], "sergei", 16 ); Q_strncpyz( s_serveroptions.playerNameBuffers[7], "assassin", 16 ); Q_strncpyz( s_serveroptions.playerNameBuffers[8], "grunt", 16 ); Q_strncpyz( s_serveroptions.playerNameBuffers[9], "skelebot", 16 ); if( s_serveroptions.gametype != GT_TEAM ) { s_serveroptions.playerType[9].curvalue = 2; } Q_strncpyz( s_serveroptions.playerNameBuffers[10], "merman", 16 ); s_serveroptions.playerType[10].curvalue = 2; Q_strncpyz( s_serveroptions.playerNameBuffers[11], "skelebot", 16 ); s_serveroptions.playerType[11].curvalue = 2; return; } count = 1; // skip the first slot, reserved for a human // get info for this map arenaInfo = UI_GetArenaInfoByMap( s_serveroptions.mapnamebuffer ); // get the bot info - we'll seed with them if any are listed Q_strncpyz( bots, Info_ValueForKey( arenaInfo, "bots" ), sizeof(bots) ); p = &bots[0]; while( *p && count < PLAYER_SLOTS ) { //skip spaces while( *p && *p == ' ' ) { p++; } if( !p ) { break; } // mark start of bot name bot = p; // skip until space of null while( *p && *p != ' ' ) { p++; } if( *p ) { *p++ = 0; } botInfo = UI_GetBotInfoByName( bot ); bot = Info_ValueForKey( botInfo, "name" ); if(!Q_stricmp(bot,"")) bot = "Sarge"; Q_strncpyz( s_serveroptions.playerNameBuffers[count], bot, sizeof(s_serveroptions.playerNameBuffers[count]) ); count++; } // set the rest of the bot slots to to other bots for( n = count; n < PLAYER_SLOTS; n++ ) { switch(n%4){ case 0: strcpy( s_serveroptions.playerNameBuffers[n], "Grunt" ); break; case 1: strcpy( s_serveroptions.playerNameBuffers[n], "Merman" ); break; case 2: strcpy( s_serveroptions.playerNameBuffers[n], "Kyonshi" ); break; default: strcpy( s_serveroptions.playerNameBuffers[n], "Skelebot" ); } } // pad up to #8 as open slots for( ;count < 8; count++ ) { s_serveroptions.playerType[count].curvalue = 0; } // close off the rest by default for( ;count < PLAYER_SLOTS; count++ ) { if( s_serveroptions.playerType[count].curvalue == 1 ) { s_serveroptions.playerType[count].curvalue = 2; } } } /* ================= ServerOptions_SetMenuItems ================= */ static void ServerOptions_SetMenuItems( void ) { static char picname[64]; const char *info; switch( s_serveroptions.gametype ) { case GT_FFA: default: Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_timelimit" ) ) ); break; case GT_TOURNAMENT: Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_timelimit" ) ) ); break; case GT_TEAM: Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_fraglimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_team_friendly" ) ); break; case GT_CTF: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_friendly" ) ); break; case GT_1FCTF: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_1fctf_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_1fctf_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_1fctf_friendly" ) ); break; case GT_OBELISK: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_overload_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_overload_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_overload_friendly" ) ); break; case GT_HARVESTER: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_harvester_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_harvester_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_harvester_friendly" ) ); break; case GT_ELIMINATION: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_elimination_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_elimination_timelimit" ) ) ); //s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_elimination_friendly" ) ); break; case GT_CTF_ELIMINATION: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_elimination_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_elimination_timelimit" ) ) ); //s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_elimination_friendly" ) ); break; case GT_LMS: Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_lms_fraglimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_lms_timelimit" ) ) ); break; case GT_DOUBLE_D: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_dd_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_dd_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_dd_friendly" ) ); break; case GT_DOMINATION: Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_dom_capturelimit" ) ) ); Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_dom_timelimit" ) ) ); s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_dom_friendly" ) ); break; } Q_strncpyz( s_serveroptions.hostname.field.buffer, UI_Cvar_VariableString( "sv_hostname" ), sizeof( s_serveroptions.hostname.field.buffer ) ); s_serveroptions.pure.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_pure" ) ); s_serveroptions.lan.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_lanforcerate" ) ); s_serveroptions.instantgib.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "g_instantgib" ) ); s_serveroptions.rockets.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "g_rockets" ) ); s_serveroptions.lmsMode.curvalue = Com_Clamp( 0, 3, trap_Cvar_VariableValue("g_lms_mode") ); s_serveroptions.oneway.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "elimination_ctf_oneway" ) ); s_serveroptions.pmove.curvalue = 0; if(trap_Cvar_VariableValue( "pmove_fixed" )) s_serveroptions.pmove.curvalue = 1; if(trap_Cvar_VariableValue( "pmove_fixed" ) && trap_Cvar_VariableValue( "pmove_msec" )==11) s_serveroptions.pmove.curvalue = 2; if(trap_Cvar_VariableValue( "pmove_float" )) s_serveroptions.pmove.curvalue = 3; // set the map pic info = UI_GetArenaInfoByNumber(s_startserver.maplist[s_startserver.currentmap]); Com_sprintf( picname, 64, "levelshots/%s", Info_ValueForKey( info, "map") ); s_serveroptions.mappic.generic.name = picname; // set the map name strcpy( s_serveroptions.mapnamebuffer, s_startserver.mapname.string ); Q_strupr( s_serveroptions.mapnamebuffer ); // get the player selections initialized ServerOptions_InitPlayerItems(); ServerOptions_SetPlayerItems(); // seed bot names ServerOptions_InitBotNames(); ServerOptions_SetPlayerItems(); } /* ================= PlayerName_Draw ================= */ static void PlayerName_Draw( void *item ) { menutext_s *s; float *color; int x, y; int style; qboolean focus; s = (menutext_s *)item; x = s->generic.x; y = s->generic.y; style = UI_SMALLFONT; focus = (s->generic.parent->cursor == s->generic.menuPosition); if ( s->generic.flags & QMF_GRAYED ) color = text_color_disabled; else if ( focus ) { color = text_color_highlight; style |= UI_PULSE; } else if ( s->generic.flags & QMF_BLINK ) { color = text_color_highlight; style |= UI_BLINK; } else color = text_color_normal; if ( focus ) { // draw cursor UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color); } UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color ); UI_DrawString( x + SMALLCHAR_WIDTH, y, s->string, style|UI_LEFT, color ); } /* ================= ServerOptions_MenuInit ================= */ #define OPTIONS_X 456 static void ServerOptions_MenuInit( qboolean multiplayer ) { int y; int n; memset( &s_serveroptions, 0 ,sizeof(serveroptions_t) ); s_serveroptions.multiplayer = multiplayer; // so the new gametypes work s_serveroptions.gametype = (int)Com_Clamp( 0, GT_MAX_GAME_TYPE - 1, trap_Cvar_VariableValue( "g_gameType" ) ); ServerOptions_Cache(); s_serveroptions.menu.wrapAround = qtrue; s_serveroptions.menu.fullscreen = qtrue; s_serveroptions.banner.generic.type = MTYPE_BTEXT; s_serveroptions.banner.generic.x = 320; s_serveroptions.banner.generic.y = 16; s_serveroptions.banner.string = "GAME SERVER"; s_serveroptions.banner.color = color_white; s_serveroptions.banner.style = UI_CENTER; s_serveroptions.mappic.generic.type = MTYPE_BITMAP; s_serveroptions.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_serveroptions.mappic.generic.x = 352; s_serveroptions.mappic.generic.y = 80; s_serveroptions.mappic.width = 160; s_serveroptions.mappic.height = 120; s_serveroptions.mappic.errorpic = GAMESERVER_UNKNOWNMAP; s_serveroptions.mappic.generic.ownerdraw = ServerOptions_LevelshotDraw; s_serveroptions.picframe.generic.type = MTYPE_BITMAP; s_serveroptions.picframe.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE|QMF_HIGHLIGHT; s_serveroptions.picframe.generic.x = 352 - 38; s_serveroptions.picframe.generic.y = 80 - 40; s_serveroptions.picframe.width = 320; s_serveroptions.picframe.height = 320; s_serveroptions.picframe.focuspic = GAMESERVER_SELECT; y = 268; if( s_serveroptions.gametype < GT_CTF || s_serveroptions.gametype== GT_LMS) { s_serveroptions.fraglimit.generic.type = MTYPE_FIELD; s_serveroptions.fraglimit.generic.name = "Frag Limit:"; s_serveroptions.fraglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.fraglimit.generic.x = OPTIONS_X; s_serveroptions.fraglimit.generic.y = y; s_serveroptions.fraglimit.generic.statusbar = ServerOptions_StatusBar; s_serveroptions.fraglimit.field.widthInChars = 3; s_serveroptions.fraglimit.field.maxchars = 3; } else { s_serveroptions.flaglimit.generic.type = MTYPE_FIELD; s_serveroptions.flaglimit.generic.name = "Capture Limit:"; s_serveroptions.flaglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.flaglimit.generic.x = OPTIONS_X; s_serveroptions.flaglimit.generic.y = y; s_serveroptions.flaglimit.generic.statusbar = ServerOptions_StatusBar; s_serveroptions.flaglimit.field.widthInChars = 3; s_serveroptions.flaglimit.field.maxchars = 3; } y += BIGCHAR_HEIGHT+2; s_serveroptions.timelimit.generic.type = MTYPE_FIELD; s_serveroptions.timelimit.generic.name = "Time Limit:"; s_serveroptions.timelimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.timelimit.generic.x = OPTIONS_X; s_serveroptions.timelimit.generic.y = y; s_serveroptions.timelimit.generic.statusbar = ServerOptions_StatusBar; s_serveroptions.timelimit.field.widthInChars = 3; s_serveroptions.timelimit.field.maxchars = 3; if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS && s_serveroptions.gametype != GT_ELIMINATION && s_serveroptions.gametype != GT_CTF_ELIMINATION) { y += BIGCHAR_HEIGHT+2; s_serveroptions.friendlyfire.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.friendlyfire.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.friendlyfire.generic.x = OPTIONS_X; s_serveroptions.friendlyfire.generic.y = y; s_serveroptions.friendlyfire.generic.name = "Friendly Fire:"; } if( s_serveroptions.gametype == GT_CTF_ELIMINATION) { y += BIGCHAR_HEIGHT+2; s_serveroptions.oneway.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.oneway.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.oneway.generic.x = OPTIONS_X; s_serveroptions.oneway.generic.y = y; s_serveroptions.oneway.generic.name = "Oneway attack:"; s_serveroptions.oneway.generic.statusbar = ServerOptions_StatusBar_Oneway; } y += BIGCHAR_HEIGHT+2; s_serveroptions.pure.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.pure.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.pure.generic.x = OPTIONS_X; s_serveroptions.pure.generic.y = y; s_serveroptions.pure.generic.name = "Pure Server:"; s_serveroptions.pure.generic.statusbar = ServerOptions_StatusBar_Pure; if( s_serveroptions.multiplayer ) { y += BIGCHAR_HEIGHT+2; s_serveroptions.lan.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.lan.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.lan.generic.x = OPTIONS_X; s_serveroptions.lan.generic.y = y; s_serveroptions.lan.generic.name = "Optimize for LAN:"; } //Insantgib option y += BIGCHAR_HEIGHT+2; s_serveroptions.instantgib.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.instantgib.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.instantgib.generic.x = OPTIONS_X; s_serveroptions.instantgib.generic.y = y; s_serveroptions.instantgib.generic.name = "Instantgib:"; s_serveroptions.instantgib.generic.statusbar = ServerOptions_StatusBar_Instantgib; //Rockets option y += BIGCHAR_HEIGHT+2; s_serveroptions.rockets.generic.type = MTYPE_RADIOBUTTON; s_serveroptions.rockets.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.rockets.generic.x = OPTIONS_X; s_serveroptions.rockets.generic.y = y; s_serveroptions.rockets.generic.name = "All rockets:"; s_serveroptions.rockets.generic.statusbar = ServerOptions_StatusBar_Allrockets; if( s_serveroptions.gametype == GT_LMS ) { y += BIGCHAR_HEIGHT+2; s_serveroptions.lmsMode.generic.type = MTYPE_SPINCONTROL; s_serveroptions.lmsMode.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.lmsMode.generic.name = "Score mode:"; s_serveroptions.lmsMode.generic.x = OPTIONS_X; //32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH; s_serveroptions.lmsMode.generic.y = y; s_serveroptions.lmsMode.itemnames = lmsMode_list; //s_serveroptions.lmsMode.curvalue = 0; } y += BIGCHAR_HEIGHT+2; s_serveroptions.pmove.generic.type = MTYPE_SPINCONTROL; s_serveroptions.pmove.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.pmove.generic.name = "Physics:"; s_serveroptions.pmove.generic.x = OPTIONS_X; //32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH; s_serveroptions.pmove.generic.y = y; s_serveroptions.pmove.itemnames = pmove_list; s_serveroptions.pmove.generic.statusbar = ServerOptions_StatusBar_Pmove; if( s_serveroptions.multiplayer ) { y += BIGCHAR_HEIGHT+2; s_serveroptions.hostname.generic.type = MTYPE_FIELD; s_serveroptions.hostname.generic.name = "Hostname:"; s_serveroptions.hostname.generic.flags = QMF_SMALLFONT; s_serveroptions.hostname.generic.x = OPTIONS_X; s_serveroptions.hostname.generic.y = y; s_serveroptions.hostname.field.widthInChars = 18; s_serveroptions.hostname.field.maxchars = 64; } y = 80; s_serveroptions.botSkill.generic.type = MTYPE_SPINCONTROL; s_serveroptions.botSkill.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_serveroptions.botSkill.generic.name = "Bot Skill:"; s_serveroptions.botSkill.generic.x = 32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH; s_serveroptions.botSkill.generic.y = y; s_serveroptions.botSkill.itemnames = botSkill_list; s_serveroptions.botSkill.curvalue = 1; y += ( 2 * SMALLCHAR_HEIGHT ); s_serveroptions.player0.generic.type = MTYPE_TEXT; s_serveroptions.player0.generic.flags = QMF_SMALLFONT; s_serveroptions.player0.generic.x = 32 + SMALLCHAR_WIDTH; s_serveroptions.player0.generic.y = y; s_serveroptions.player0.color = color_orange; s_serveroptions.player0.style = UI_LEFT|UI_SMALLFONT; for( n = 0; n < PLAYER_SLOTS; n++ ) { s_serveroptions.playerType[n].generic.type = MTYPE_SPINCONTROL; s_serveroptions.playerType[n].generic.flags = QMF_SMALLFONT; s_serveroptions.playerType[n].generic.id = ID_PLAYER_TYPE; s_serveroptions.playerType[n].generic.callback = ServerOptions_Event; s_serveroptions.playerType[n].generic.x = 32; s_serveroptions.playerType[n].generic.y = y; s_serveroptions.playerType[n].itemnames = playerType_list; s_serveroptions.playerName[n].generic.type = MTYPE_TEXT; s_serveroptions.playerName[n].generic.flags = QMF_SMALLFONT; s_serveroptions.playerName[n].generic.x = 96; s_serveroptions.playerName[n].generic.y = y; s_serveroptions.playerName[n].generic.callback = ServerOptions_PlayerNameEvent; s_serveroptions.playerName[n].generic.id = n; s_serveroptions.playerName[n].generic.ownerdraw = PlayerName_Draw; s_serveroptions.playerName[n].color = color_orange; s_serveroptions.playerName[n].style = UI_SMALLFONT; s_serveroptions.playerName[n].string = s_serveroptions.playerNameBuffers[n]; s_serveroptions.playerName[n].generic.top = s_serveroptions.playerName[n].generic.y; s_serveroptions.playerName[n].generic.bottom = s_serveroptions.playerName[n].generic.y + SMALLCHAR_HEIGHT; s_serveroptions.playerName[n].generic.left = s_serveroptions.playerName[n].generic.x - SMALLCHAR_HEIGHT/ 2; s_serveroptions.playerName[n].generic.right = s_serveroptions.playerName[n].generic.x + 16 * SMALLCHAR_WIDTH; s_serveroptions.playerTeam[n].generic.type = MTYPE_SPINCONTROL; s_serveroptions.playerTeam[n].generic.flags = QMF_SMALLFONT; s_serveroptions.playerTeam[n].generic.x = 240; s_serveroptions.playerTeam[n].generic.y = y; s_serveroptions.playerTeam[n].itemnames = playerTeam_list; y += ( SMALLCHAR_HEIGHT + 4 ); } s_serveroptions.back.generic.type = MTYPE_BITMAP; s_serveroptions.back.generic.name = GAMESERVER_BACK0; s_serveroptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_serveroptions.back.generic.callback = ServerOptions_Event; s_serveroptions.back.generic.id = ID_BACK; s_serveroptions.back.generic.x = 0; s_serveroptions.back.generic.y = 480-64; s_serveroptions.back.width = 128; s_serveroptions.back.height = 64; s_serveroptions.back.focuspic = GAMESERVER_BACK1; s_serveroptions.next.generic.type = MTYPE_BITMAP; s_serveroptions.next.generic.name = GAMESERVER_NEXT0; s_serveroptions.next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE|QMF_GRAYED|QMF_HIDDEN; s_serveroptions.next.generic.callback = ServerOptions_Event; s_serveroptions.next.generic.id = ID_STARTSERVERNEXT; s_serveroptions.next.generic.x = 640; s_serveroptions.next.generic.y = 480-64-72; s_serveroptions.next.generic.statusbar = ServerOptions_StatusBar; s_serveroptions.next.width = 128; s_serveroptions.next.height = 64; s_serveroptions.next.focuspic = GAMESERVER_NEXT1; s_serveroptions.go.generic.type = MTYPE_BITMAP; s_serveroptions.go.generic.name = GAMESERVER_FIGHT0; s_serveroptions.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_serveroptions.go.generic.callback = ServerOptions_Event; s_serveroptions.go.generic.id = ID_GO; s_serveroptions.go.generic.x = 640; s_serveroptions.go.generic.y = 480-64; s_serveroptions.go.width = 128; s_serveroptions.go.height = 64; s_serveroptions.go.focuspic = GAMESERVER_FIGHT1; Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.banner ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.mappic ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.picframe ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.botSkill ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.player0 ); for( n = 0; n < PLAYER_SLOTS; n++ ) { if( n != 0 ) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerType[n] ); } Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerName[n] ); if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS ) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerTeam[n] ); } } if( s_serveroptions.gametype < GT_CTF || s_serveroptions.gametype == GT_LMS ) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.fraglimit ); } else { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.flaglimit ); } Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.timelimit ); if( s_serveroptions.gametype >= GT_TEAM && s_serveroptions.gametype != GT_LMS && s_serveroptions.gametype != GT_ELIMINATION && s_serveroptions.gametype != GT_CTF_ELIMINATION) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.friendlyfire ); } Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.pure ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.instantgib ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.rockets ); if( s_serveroptions.gametype == GT_LMS) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.lmsMode ); } if( s_serveroptions.gametype == GT_CTF_ELIMINATION) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.oneway ); } Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.pmove ); if( s_serveroptions.multiplayer ) { Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.lan ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.hostname ); } Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.back ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.next ); Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.go ); ServerOptions_SetMenuItems(); } /* ================= ServerOptions_Cache ================= */ void ServerOptions_Cache( void ) { trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 ); trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 ); trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT0 ); trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT1 ); trap_R_RegisterShaderNoMip( GAMESERVER_SELECT ); trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP ); } /* ================= UI_ServerOptionsMenu ================= */ static void UI_ServerOptionsMenu( qboolean multiplayer ) { ServerOptions_MenuInit( multiplayer ); UI_PushMenu( &s_serveroptions.menu ); } /* ============================================================================= BOT SELECT MENU ***** ============================================================================= */ #define BOTSELECT_BACK0 "menu/art_blueish/back_0" #define BOTSELECT_BACK1 "menu/art_blueish/back_1" #define BOTSELECT_ACCEPT0 "menu/art_blueish/accept_0" #define BOTSELECT_ACCEPT1 "menu/art_blueish/accept_1" #define BOTSELECT_SELECT "menu/art/opponents_select" #define BOTSELECT_SELECTED "menu/art/opponents_selected" #define BOTSELECT_ARROWS "menu/art_blueish/gs_arrows_0" #define BOTSELECT_ARROWSL "menu/art_blueish/gs_arrows_l" #define BOTSELECT_ARROWSR "menu/art_blueish/gs_arrows_r" #define PLAYERGRID_COLS 4 #define PLAYERGRID_ROWS 4 #define MAX_MODELSPERPAGE (PLAYERGRID_ROWS * PLAYERGRID_COLS) typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s pics[MAX_MODELSPERPAGE]; menubitmap_s picbuttons[MAX_MODELSPERPAGE]; menutext_s picnames[MAX_MODELSPERPAGE]; menubitmap_s arrows; menubitmap_s left; menubitmap_s right; menubitmap_s go; menubitmap_s back; int numBots; int modelpage; int numpages; int selectedmodel; int sortedBotNums[MAX_BOTS]; char boticons[MAX_MODELSPERPAGE][MAX_QPATH]; char botnames[MAX_MODELSPERPAGE][16]; } botSelectInfo_t; static botSelectInfo_t botSelectInfo; /* ================= UI_BotSelectMenu_SortCompare ================= */ static int QDECL UI_BotSelectMenu_SortCompare( const void *arg1, const void *arg2 ) { int num1, num2; const char *info1, *info2; const char *name1, *name2; num1 = *(int *)arg1; num2 = *(int *)arg2; info1 = UI_GetBotInfoByNumber( num1 ); info2 = UI_GetBotInfoByNumber( num2 ); name1 = Info_ValueForKey( info1, "name" ); name2 = Info_ValueForKey( info2, "name" ); return Q_stricmp( name1, name2 ); } /* ================= UI_BotSelectMenu_BuildList ================= */ static void UI_BotSelectMenu_BuildList( void ) { int n; botSelectInfo.modelpage = 0; botSelectInfo.numBots = UI_GetNumBots(); botSelectInfo.numpages = botSelectInfo.numBots / MAX_MODELSPERPAGE; if( botSelectInfo.numBots % MAX_MODELSPERPAGE ) { botSelectInfo.numpages++; } // initialize the array for( n = 0; n < botSelectInfo.numBots; n++ ) { botSelectInfo.sortedBotNums[n] = n; } // now sort it qsort( botSelectInfo.sortedBotNums, botSelectInfo.numBots, sizeof(botSelectInfo.sortedBotNums[0]), UI_BotSelectMenu_SortCompare ); } /* ================= ServerPlayerIcon ================= */ static void ServerPlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) { char *skin; char model[MAX_QPATH]; Q_strncpyz( model, modelAndSkin, sizeof(model)); skin = strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = "default"; } Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin ); if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) { Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model ); } } /* ================= UI_BotSelectMenu_UpdateGrid ================= */ static void UI_BotSelectMenu_UpdateGrid( void ) { const char *info; int i; int j; j = botSelectInfo.modelpage * MAX_MODELSPERPAGE; for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++, j++) { if( j < botSelectInfo.numBots ) { info = UI_GetBotInfoByNumber( botSelectInfo.sortedBotNums[j] ); ServerPlayerIcon( Info_ValueForKey( info, "model" ), botSelectInfo.boticons[i], MAX_QPATH ); Q_strncpyz( botSelectInfo.botnames[i], Info_ValueForKey( info, "name" ), 16 ); Q_CleanStr( botSelectInfo.botnames[i] ); botSelectInfo.pics[i].generic.name = botSelectInfo.boticons[i]; if( BotAlreadySelected( botSelectInfo.botnames[i] ) ) { botSelectInfo.picnames[i].color = color_red; } else { botSelectInfo.picnames[i].color = color_orange; } botSelectInfo.picbuttons[i].generic.flags &= ~((unsigned int)QMF_INACTIVE); } else { // dead slot botSelectInfo.pics[i].generic.name = NULL; botSelectInfo.picbuttons[i].generic.flags |= QMF_INACTIVE; botSelectInfo.botnames[i][0] = 0; } botSelectInfo.pics[i].generic.flags &= ~((unsigned int)QMF_HIGHLIGHT); botSelectInfo.pics[i].shader = 0; botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS; } // set selected model i = botSelectInfo.selectedmodel % MAX_MODELSPERPAGE; botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT; botSelectInfo.picbuttons[i].generic.flags &= ~((unsigned int)QMF_PULSEIFFOCUS); if( botSelectInfo.numpages > 1 ) { if( botSelectInfo.modelpage > 0 ) { botSelectInfo.left.generic.flags &= ~((unsigned int)QMF_INACTIVE); } else { botSelectInfo.left.generic.flags |= QMF_INACTIVE; } if( botSelectInfo.modelpage < (botSelectInfo.numpages - 1) ) { botSelectInfo.right.generic.flags &= ~((unsigned int)QMF_INACTIVE); } else { botSelectInfo.right.generic.flags |= QMF_INACTIVE; } } else { // hide left/right markers botSelectInfo.left.generic.flags |= QMF_INACTIVE; botSelectInfo.right.generic.flags |= QMF_INACTIVE; } } /* ================= UI_BotSelectMenu_Default ================= */ static void UI_BotSelectMenu_Default( char *bot ) { const char *botInfo; const char *test; int n; int i; for( n = 0; n < botSelectInfo.numBots; n++ ) { botInfo = UI_GetBotInfoByNumber( n ); test = Info_ValueForKey( botInfo, "name" ); if( Q_stricmp( bot, test ) == 0 ) { break; } } if( n == botSelectInfo.numBots ) { botSelectInfo.selectedmodel = 0; return; } for( i = 0; i < botSelectInfo.numBots; i++ ) { if( botSelectInfo.sortedBotNums[i] == n ) { break; } } if( i == botSelectInfo.numBots ) { botSelectInfo.selectedmodel = 0; return; } botSelectInfo.selectedmodel = i; } /* ================= UI_BotSelectMenu_LeftEvent ================= */ static void UI_BotSelectMenu_LeftEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } if( botSelectInfo.modelpage > 0 ) { botSelectInfo.modelpage--; botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE; UI_BotSelectMenu_UpdateGrid(); } } /* ================= UI_BotSelectMenu_RightEvent ================= */ static void UI_BotSelectMenu_RightEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } if( botSelectInfo.modelpage < botSelectInfo.numpages - 1 ) { botSelectInfo.modelpage++; botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE; UI_BotSelectMenu_UpdateGrid(); } } /* ================= UI_BotSelectMenu_BotEvent ================= */ static void UI_BotSelectMenu_BotEvent( void* ptr, int event ) { int i; if( event != QM_ACTIVATED ) { return; } for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++ ) { botSelectInfo.pics[i].generic.flags &= ~((unsigned int)QMF_HIGHLIGHT); botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS; } // set selected i = ((menucommon_s*)ptr)->id; botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT; botSelectInfo.picbuttons[i].generic.flags &= ~((unsigned int)QMF_PULSEIFFOCUS); botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE + i; } /* ================= UI_BotSelectMenu_BackEvent ================= */ static void UI_BotSelectMenu_BackEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); } /* ================= UI_BotSelectMenu_SelectEvent ================= */ static void UI_BotSelectMenu_SelectEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); s_serveroptions.newBot = qtrue; Q_strncpyz( s_serveroptions.newBotName, botSelectInfo.botnames[botSelectInfo.selectedmodel % MAX_MODELSPERPAGE], 16 ); } /* ================= UI_BotSelectMenu_Cache ================= */ void UI_BotSelectMenu_Cache( void ) { trap_R_RegisterShaderNoMip( BOTSELECT_BACK0 ); trap_R_RegisterShaderNoMip( BOTSELECT_BACK1 ); trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT0 ); trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT1 ); trap_R_RegisterShaderNoMip( BOTSELECT_SELECT ); trap_R_RegisterShaderNoMip( BOTSELECT_SELECTED ); trap_R_RegisterShaderNoMip( BOTSELECT_ARROWS ); trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSL ); trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSR ); } static void UI_BotSelectMenu_Init( char *bot ) { int i, j, k; int x, y; memset( &botSelectInfo, 0 ,sizeof(botSelectInfo) ); botSelectInfo.menu.wrapAround = qtrue; botSelectInfo.menu.fullscreen = qtrue; UI_BotSelectMenu_Cache(); botSelectInfo.banner.generic.type = MTYPE_BTEXT; botSelectInfo.banner.generic.x = 320; botSelectInfo.banner.generic.y = 16; botSelectInfo.banner.string = "SELECT BOT"; botSelectInfo.banner.color = color_white; botSelectInfo.banner.style = UI_CENTER; y = 80; for( i = 0, k = 0; i < PLAYERGRID_ROWS; i++) { x = 180; for( j = 0; j < PLAYERGRID_COLS; j++, k++ ) { botSelectInfo.pics[k].generic.type = MTYPE_BITMAP; botSelectInfo.pics[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; botSelectInfo.pics[k].generic.x = x; botSelectInfo.pics[k].generic.y = y; botSelectInfo.pics[k].generic.name = botSelectInfo.boticons[k]; botSelectInfo.pics[k].width = 64; botSelectInfo.pics[k].height = 64; botSelectInfo.pics[k].focuspic = BOTSELECT_SELECTED; botSelectInfo.pics[k].focuscolor = colorRed; botSelectInfo.picbuttons[k].generic.type = MTYPE_BITMAP; botSelectInfo.picbuttons[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_NODEFAULTINIT|QMF_PULSEIFFOCUS; botSelectInfo.picbuttons[k].generic.callback = UI_BotSelectMenu_BotEvent; botSelectInfo.picbuttons[k].generic.id = k; botSelectInfo.picbuttons[k].generic.x = x - 16; botSelectInfo.picbuttons[k].generic.y = y - 16; botSelectInfo.picbuttons[k].generic.left = x; botSelectInfo.picbuttons[k].generic.top = y; botSelectInfo.picbuttons[k].generic.right = x + 64; botSelectInfo.picbuttons[k].generic.bottom = y + 64; botSelectInfo.picbuttons[k].width = 128; botSelectInfo.picbuttons[k].height = 128; botSelectInfo.picbuttons[k].focuspic = BOTSELECT_SELECT; botSelectInfo.picbuttons[k].focuscolor = colorRed; botSelectInfo.picnames[k].generic.type = MTYPE_TEXT; botSelectInfo.picnames[k].generic.flags = QMF_SMALLFONT; botSelectInfo.picnames[k].generic.x = x + 32; botSelectInfo.picnames[k].generic.y = y + 64; botSelectInfo.picnames[k].string = botSelectInfo.botnames[k]; botSelectInfo.picnames[k].color = color_orange; botSelectInfo.picnames[k].style = UI_CENTER|UI_SMALLFONT; x += (64 + 6); } y += (64 + SMALLCHAR_HEIGHT + 6); } botSelectInfo.arrows.generic.type = MTYPE_BITMAP; botSelectInfo.arrows.generic.name = BOTSELECT_ARROWS; botSelectInfo.arrows.generic.flags = QMF_INACTIVE; botSelectInfo.arrows.generic.x = 260; botSelectInfo.arrows.generic.y = 440; botSelectInfo.arrows.width = 128; botSelectInfo.arrows.height = 32; botSelectInfo.left.generic.type = MTYPE_BITMAP; botSelectInfo.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; botSelectInfo.left.generic.callback = UI_BotSelectMenu_LeftEvent; botSelectInfo.left.generic.x = 260; botSelectInfo.left.generic.y = 440; botSelectInfo.left.width = 64; botSelectInfo.left.height = 32; botSelectInfo.left.focuspic = BOTSELECT_ARROWSL; botSelectInfo.right.generic.type = MTYPE_BITMAP; botSelectInfo.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; botSelectInfo.right.generic.callback = UI_BotSelectMenu_RightEvent; botSelectInfo.right.generic.x = 321; botSelectInfo.right.generic.y = 440; botSelectInfo.right.width = 64; botSelectInfo.right.height = 32; botSelectInfo.right.focuspic = BOTSELECT_ARROWSR; botSelectInfo.back.generic.type = MTYPE_BITMAP; botSelectInfo.back.generic.name = BOTSELECT_BACK0; botSelectInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; botSelectInfo.back.generic.callback = UI_BotSelectMenu_BackEvent; botSelectInfo.back.generic.x = 0; botSelectInfo.back.generic.y = 480-64; botSelectInfo.back.width = 128; botSelectInfo.back.height = 64; botSelectInfo.back.focuspic = BOTSELECT_BACK1; botSelectInfo.go.generic.type = MTYPE_BITMAP; botSelectInfo.go.generic.name = BOTSELECT_ACCEPT0; botSelectInfo.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; botSelectInfo.go.generic.callback = UI_BotSelectMenu_SelectEvent; botSelectInfo.go.generic.x = 640; botSelectInfo.go.generic.y = 480-64; botSelectInfo.go.width = 128; botSelectInfo.go.height = 64; botSelectInfo.go.focuspic = BOTSELECT_ACCEPT1; Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.banner ); for( i = 0; i < MAX_MODELSPERPAGE; i++ ) { Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.pics[i] ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picbuttons[i] ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picnames[i] ); } Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.arrows ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.left ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.right ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.back ); Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.go ); UI_BotSelectMenu_BuildList(); UI_BotSelectMenu_Default( bot ); botSelectInfo.modelpage = botSelectInfo.selectedmodel / MAX_MODELSPERPAGE; UI_BotSelectMenu_UpdateGrid(); } /* ================= UI_BotSelectMenu ================= */ void UI_BotSelectMenu( char *bot ) { UI_BotSelectMenu_Init( bot ); UI_PushMenu( &botSelectInfo.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_specifyleague.c0000644000175000017500000002460711656310261020654 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /********************************************************************************* SPECIFY SERVER *********************************************************************************/ #define MAX_LISTBOXITEMS 128 #define MAX_LISTBOXWIDTH 40 #define MAX_LEAGUENAME 80 #define SPECIFYLEAGUE_FRAMEL "menu/art_blueish/frame2_l" #define SPECIFYLEAGUE_FRAMER "menu/art_blueish/frame1_r" #define SPECIFYLEAGUE_BACK0 "menu/art_blueish/back_0" #define SPECIFYLEAGUE_BACK1 "menu/art_blueish/back_1" #define SPECIFYLEAGUE_ARROWS0 "menu/art_blueish/arrows_vert_0" #define SPECIFYLEAGUE_UP "menu/art_blueish/arrows_vert_top" #define SPECIFYLEAGUE_DOWN "menu/art_blueish/arrows_vert_bot" #define GLOBALRANKINGS_LOGO "menu/art/gr/grlogo" #define GLOBALRANKINGS_LETTERS "menu/art/gr/grletters" #define ID_SPECIFYLEAGUENAME 100 #define ID_SPECIFYLEAGUELIST 101 #define ID_SPECIFYLEAGUEUP 102 #define ID_SPECIFYLEAGUEDOWN 103 #define ID_SPECIFYLEAGUEBACK 104 static char* specifyleague_artlist[] = { SPECIFYLEAGUE_FRAMEL, SPECIFYLEAGUE_FRAMER, SPECIFYLEAGUE_ARROWS0, SPECIFYLEAGUE_UP, SPECIFYLEAGUE_DOWN, SPECIFYLEAGUE_BACK0, SPECIFYLEAGUE_BACK1, GLOBALRANKINGS_LOGO, GLOBALRANKINGS_LETTERS, NULL }; static char playername[80]; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menufield_s rankname; menulist_s list; menubitmap_s arrows; menubitmap_s up; menubitmap_s down; menubitmap_s back; menubitmap_s grlogo; menubitmap_s grletters; } specifyleague_t; static specifyleague_t s_specifyleague; typedef struct { char buff[MAX_LISTBOXWIDTH]; char leaguename[MAX_LEAGUENAME]; } table_t; table_t league_table[MAX_LISTBOXITEMS]; char *leaguename_items[MAX_LISTBOXITEMS]; static void SpecifyLeague_GetList() { int count = 0; int i; /* The Player Name has changed. We need to perform another search */ Q_strncpyz( playername, s_specifyleague.rankname.field.buffer, sizeof(playername) ); count = trap_CL_UI_RankGetLeauges( playername ); for(i = 0; i < count; i++) { char s[MAX_LEAGUENAME]; const char *var; var = va( "leaguename%i", i+1 ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); Q_strncpyz(league_table[i].leaguename, s, sizeof(league_table[i].leaguename) ); Q_strncpyz(league_table[i].buff, league_table[i].leaguename, sizeof(league_table[i].buff) ); } s_specifyleague.list.numitems = count; } /* ================= SpecifyLeague_Event ================= */ static void SpecifyLeague_Event( void* ptr, int event ) { int id; id = ((menucommon_s*)ptr)->id; //if( event != QM_ACTIVATED && id != ID_SPECIFYLEAGUELIST ) { // return; //} switch (id) { case ID_SPECIFYLEAGUELIST: if( event == QM_GOTFOCUS ) { //ArenaServers_UpdatePicture(); } break; case ID_SPECIFYLEAGUEUP: if( event == QM_ACTIVATED ) ScrollList_Key( &s_specifyleague.list, K_UPARROW ); break; case ID_SPECIFYLEAGUEDOWN: if( event == QM_ACTIVATED ) ScrollList_Key( &s_specifyleague.list, K_DOWNARROW ); break; case ID_SPECIFYLEAGUENAME: if( (event == QM_LOSTFOCUS) && (Q_strncmp(playername, s_specifyleague.rankname.field.buffer, strlen(s_specifyleague.rankname.field.buffer)) != 0)) { SpecifyLeague_GetList(); } break; case ID_SPECIFYLEAGUEBACK: if( event == QM_ACTIVATED ) { trap_Cvar_Set( "sv_leagueName", league_table[s_specifyleague.list.curvalue].leaguename); UI_PopMenu(); } break; } } /* ================= SpecifyLeague_MenuInit ================= */ void SpecifyLeague_MenuInit( void ) { int i; // zero set all our globals memset( &s_specifyleague, 0 ,sizeof(specifyleague_t) ); SpecifyLeague_Cache(); s_specifyleague.menu.wrapAround = qtrue; s_specifyleague.menu.fullscreen = qtrue; s_specifyleague.banner.generic.type = MTYPE_BTEXT; s_specifyleague.banner.generic.x = 320; s_specifyleague.banner.generic.y = 16; s_specifyleague.banner.string = "CHOOSE LEAGUE"; s_specifyleague.banner.color = color_white; s_specifyleague.banner.style = UI_CENTER; s_specifyleague.framel.generic.type = MTYPE_BITMAP; s_specifyleague.framel.generic.name = SPECIFYLEAGUE_FRAMEL; s_specifyleague.framel.generic.flags = QMF_INACTIVE; s_specifyleague.framel.generic.x = 0; s_specifyleague.framel.generic.y = 78; s_specifyleague.framel.width = 256; s_specifyleague.framel.height = 334; s_specifyleague.framer.generic.type = MTYPE_BITMAP; s_specifyleague.framer.generic.name = SPECIFYLEAGUE_FRAMER; s_specifyleague.framer.generic.flags = QMF_INACTIVE; s_specifyleague.framer.generic.x = 376; s_specifyleague.framer.generic.y = 76; s_specifyleague.framer.width = 256; s_specifyleague.framer.height = 334; s_specifyleague.grlogo.generic.type = MTYPE_BITMAP; s_specifyleague.grlogo.generic.name = GLOBALRANKINGS_LOGO; s_specifyleague.grlogo.generic.flags = QMF_INACTIVE; s_specifyleague.grlogo.generic.x = 0; s_specifyleague.grlogo.generic.y = 0; s_specifyleague.grlogo.width = 64; s_specifyleague.grlogo.height = 128; s_specifyleague.rankname.generic.type = MTYPE_FIELD; s_specifyleague.rankname.generic.name = "Player Name:"; s_specifyleague.rankname.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_specifyleague.rankname.generic.callback = SpecifyLeague_Event; s_specifyleague.rankname.generic.id = ID_SPECIFYLEAGUENAME; s_specifyleague.rankname.generic.x = 226; s_specifyleague.rankname.generic.y = 128; s_specifyleague.rankname.field.widthInChars = 32; s_specifyleague.rankname.field.maxchars = 80; s_specifyleague.list.generic.type = MTYPE_SCROLLLIST; s_specifyleague.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS; s_specifyleague.list.generic.id = ID_SPECIFYLEAGUELIST; s_specifyleague.list.generic.callback = SpecifyLeague_Event; s_specifyleague.list.generic.x = 160; s_specifyleague.list.generic.y = 200; s_specifyleague.list.width = MAX_LISTBOXWIDTH; s_specifyleague.list.height = 8; s_specifyleague.list.itemnames = (const char **)leaguename_items; s_specifyleague.list.numitems = 0; for( i = 0; i < MAX_LISTBOXITEMS; i++ ) { league_table[i].buff[0] = 0; league_table[i].leaguename[0] = 0; leaguename_items[i] = league_table[i].buff; } s_specifyleague.arrows.generic.type = MTYPE_BITMAP; s_specifyleague.arrows.generic.name = SPECIFYLEAGUE_ARROWS0; s_specifyleague.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_specifyleague.arrows.generic.callback = SpecifyLeague_Event; s_specifyleague.arrows.generic.x = 512; s_specifyleague.arrows.generic.y = 240-64+16; s_specifyleague.arrows.width = 64; s_specifyleague.arrows.height = 128; s_specifyleague.up.generic.type = MTYPE_BITMAP; s_specifyleague.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_specifyleague.up.generic.callback = SpecifyLeague_Event; s_specifyleague.up.generic.id = ID_SPECIFYLEAGUEUP; s_specifyleague.up.generic.x = 512; s_specifyleague.up.generic.y = 240-64+16; s_specifyleague.up.width = 64; s_specifyleague.up.height = 64; s_specifyleague.up.focuspic = SPECIFYLEAGUE_UP; s_specifyleague.down.generic.type = MTYPE_BITMAP; s_specifyleague.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_specifyleague.down.generic.callback = SpecifyLeague_Event; s_specifyleague.down.generic.id = ID_SPECIFYLEAGUEDOWN; s_specifyleague.down.generic.x = 512; s_specifyleague.down.generic.y = 240+16; s_specifyleague.down.width = 64; s_specifyleague.down.height = 64; s_specifyleague.down.focuspic = SPECIFYLEAGUE_DOWN; s_specifyleague.back.generic.type = MTYPE_BITMAP; s_specifyleague.back.generic.name = SPECIFYLEAGUE_BACK0; s_specifyleague.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_specifyleague.back.generic.callback = SpecifyLeague_Event; s_specifyleague.back.generic.id = ID_SPECIFYLEAGUEBACK; s_specifyleague.back.generic.x = 0; s_specifyleague.back.generic.y = 480-64; s_specifyleague.back.width = 128; s_specifyleague.back.height = 64; s_specifyleague.back.focuspic = SPECIFYLEAGUE_BACK1; Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.banner ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.framel ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.framer ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.grlogo ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.rankname ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.list ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.arrows ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.up ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.down ); Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.back ); // initialize any menu variables Q_strncpyz( s_specifyleague.rankname.field.buffer, UI_Cvar_VariableString("name"), sizeof(s_specifyleague.rankname.field.buffer) ); Q_strncpyz( playername, UI_Cvar_VariableString("name"), sizeof(playername) ); SpecifyLeague_GetList(); } /* ================= SpecifyLeague_Cache ================= */ void SpecifyLeague_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!specifyleague_artlist[i]) break; trap_R_RegisterShaderNoMip(specifyleague_artlist[i]); } } /* ================= UI_SpecifyLeagueMenu ================= */ void UI_SpecifyLeagueMenu( void ) { SpecifyLeague_MenuInit(); UI_PushMenu( &s_specifyleague.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_gameinfo.c0000644000175000017500000004047511656310261017615 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // gameinfo.c // #include "ui_local.h" // // arena and bot info // #define POOLSIZE 128 * 1024 int ui_numBots; static char *ui_botInfos[MAX_BOTS]; static int ui_numArenas; static char *ui_arenaInfos[MAX_ARENAS]; static int ui_numSinglePlayerArenas; static int ui_numSpecialSinglePlayerArenas; static char memoryPool[POOLSIZE]; static int allocPoint, outOfMemory; /* =============== UI_Alloc =============== */ void *UI_Alloc( int size ) { char *p; if ( allocPoint + size > POOLSIZE ) { outOfMemory = qtrue; return NULL; } p = &memoryPool[allocPoint]; allocPoint += ( size + 31 ) & ~31; return p; } /* =============== UI_InitMemory =============== */ void UI_InitMemory( void ) { allocPoint = 0; outOfMemory = qfalse; } /* =============== UI_ParseInfos =============== */ int UI_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "" ); } Info_SetValueForKey( info, key, token ); } //NOTE: extra space for arena number infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); if (infos[count]) { strcpy(infos[count], info); count++; } } return count; } /* =============== UI_LoadArenasFromFile =============== */ static void UI_LoadArenasFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_ARENAS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_ARENAS_TEXT ) { trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_ARENAS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] ); } /* =============== UI_LoadArenas =============== */ static void UI_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[20*1024]; char* dirptr; int i, n; int dirlen; char *type; char *tag; int singlePlayerNum, specialNum, otherNum; ui_numArenas = 0; trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { UI_LoadArenasFromFile(arenasFile.string); } else { UI_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, sizeof(dirlist) ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadArenasFromFile(filename); } trap_Print( va( "%i arenas parsed\n", ui_numArenas ) ); if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n"); // set initial numbers for( n = 0; n < ui_numArenas; n++ ) { Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", n ) ); } // go through and count single players levels ui_numSinglePlayerArenas = 0; ui_numSpecialSinglePlayerArenas = 0; for( n = 0; n < ui_numArenas; n++ ) { // determine type type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if( !*type ) { continue; } if( strstr( type, "single" ) ) { // check for special single player arenas (training, final) tag = Info_ValueForKey( ui_arenaInfos[n], "special" ); if( *tag ) { ui_numSpecialSinglePlayerArenas++; continue; } ui_numSinglePlayerArenas++; } } n = ui_numSinglePlayerArenas % ARENAS_PER_TIER; if( n != 0 ) { ui_numSinglePlayerArenas -= n; trap_Print( va( "%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER ) ); } // go through once more and assign number to the levels singlePlayerNum = 0; specialNum = singlePlayerNum + ui_numSinglePlayerArenas; otherNum = specialNum + ui_numSpecialSinglePlayerArenas + n; for( n = 0; n < ui_numArenas; n++ ) { // determine type type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if( *type ) { if( strstr( type, "single" ) ) { // check for special single player arenas (training, final) tag = Info_ValueForKey( ui_arenaInfos[n], "special" ); if( *tag ) { Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", specialNum++ ) ); continue; } Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", singlePlayerNum++ ) ); continue; } } Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", otherNum++ ) ); } } /* =============== UI_GetArenaInfoByNumber =============== */ const char *UI_GetArenaInfoByNumber( int num ) { int n; char *value; if( num < 0 || num >= ui_numArenas ) { trap_Print( va( S_COLOR_RED "Invalid arena number: %i\n", num ) ); return NULL; } for( n = 0; n < ui_numArenas; n++ ) { value = Info_ValueForKey( ui_arenaInfos[n], "num" ); if( *value && atoi(value) == num ) { return ui_arenaInfos[n]; } } return NULL; } /* =============== UI_GetArenaInfoByNumber =============== */ const char *UI_GetArenaInfoByMap( const char *map ) { int n; for( n = 0; n < ui_numArenas; n++ ) { if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "map" ), map ) == 0 ) { return ui_arenaInfos[n]; } } return NULL; } /* =============== UI_GetSpecialArenaInfo =============== */ const char *UI_GetSpecialArenaInfo( const char *tag ) { int n; for( n = 0; n < ui_numArenas; n++ ) { if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "special" ), tag ) == 0 ) { return ui_arenaInfos[n]; } } return NULL; } /* =============== UI_LoadBotsFromFile =============== */ static void UI_LoadBotsFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_BOTS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_BOTS_TEXT ) { trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_BOTS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] ); if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all bots\n"); } /* =============== UI_LoadBots =============== */ static void UI_LoadBots( void ) { vmCvar_t botsFile; int numdirs; char filename[128]; char dirlist[1024]; char* dirptr; int i; int dirlen; ui_numBots = 0; trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); if( *botsFile.string ) { UI_LoadBotsFromFile(botsFile.string); } else { UI_LoadBotsFromFile("scripts/bots.txt"); } // get all bots from .bot files numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadBotsFromFile(filename); } trap_Print( va( "%i bots parsed\n", ui_numBots ) ); } /* =============== UI_GetBotInfoByNumber =============== */ char *UI_GetBotInfoByNumber( int num ) { if( num < 0 || num >= ui_numBots ) { trap_Print( va( S_COLOR_RED "Invalid bot number: %i\n", num ) ); return NULL; } return ui_botInfos[num]; } /* =============== UI_GetBotInfoByName =============== */ char *UI_GetBotInfoByName( const char *name ) { int n; char *value; for ( n = 0; n < ui_numBots ; n++ ) { value = Info_ValueForKey( ui_botInfos[n], "name" ); if ( !Q_stricmp( value, name ) ) { return ui_botInfos[n]; } } return NULL; } // // single player game info // /* =============== UI_GetBestScore Returns the player's best finish on a given level, 0 if the have not played the level =============== */ void UI_GetBestScore( int level, int *score, int *skill ) { int n; int skillScore; int bestScore; int bestScoreSkill; char arenaKey[16]; char scores[MAX_INFO_VALUE]; if( !score || !skill ) { return; } if( level < 0 || level > ui_numArenas ) { return; } bestScore = 0; bestScoreSkill = 0; for( n = 1; n <= 5; n++ ) { trap_Cvar_VariableStringBuffer( va( "g_spScores%i", n ), scores, MAX_INFO_VALUE ); Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level ); skillScore = atoi( Info_ValueForKey( scores, arenaKey ) ); if( skillScore < 1 || skillScore > 8 ) { continue; } if( !bestScore || skillScore <= bestScore ) { bestScore = skillScore; bestScoreSkill = n; } } *score = bestScore; *skill = bestScoreSkill; } /* =============== UI_SetBestScore Set the player's best finish for a level =============== */ void UI_SetBestScore( int level, int score ) { int skill; int oldScore; char arenaKey[16]; char scores[MAX_INFO_VALUE]; // validate score if( score < 1 || score > 8 ) { return; } // validate skill skill = (int)trap_Cvar_VariableValue( "g_spSkill" ); if( skill < 1 || skill > 5 ) { return; } // get scores trap_Cvar_VariableStringBuffer( va( "g_spScores%i", skill ), scores, MAX_INFO_VALUE ); // see if this is better Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level ); oldScore = atoi( Info_ValueForKey( scores, arenaKey ) ); if( oldScore && oldScore <= score ) { return; } // update scores Info_SetValueForKey( scores, arenaKey, va( "%i", score ) ); trap_Cvar_Set( va( "g_spScores%i", skill ), scores ); } /* =============== UI_LogAwardData =============== */ void UI_LogAwardData( int award, int data ) { char key[16]; char awardData[MAX_INFO_VALUE]; int oldValue; if( data == 0 ) { return; } if( award > AWARD_PERFECT ) { trap_Print( va( S_COLOR_RED "Bad award %i in UI_LogAwardData\n", award ) ); return; } trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) ); Com_sprintf( key, sizeof(key), "a%i", award ); oldValue = atoi( Info_ValueForKey( awardData, key ) ); Info_SetValueForKey( awardData, key, va( "%i", oldValue + data ) ); trap_Cvar_Set( "g_spAwards", awardData ); } /* =============== UI_GetAwardLevel =============== */ int UI_GetAwardLevel( int award ) { char key[16]; char awardData[MAX_INFO_VALUE]; trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) ); Com_sprintf( key, sizeof(key), "a%i", award ); return atoi( Info_ValueForKey( awardData, key ) ); } /* =============== UI_TierCompleted =============== */ int UI_TierCompleted( int levelWon ) { int level; int n; int tier; int score; int skill; const char *info; tier = levelWon / ARENAS_PER_TIER; level = tier * ARENAS_PER_TIER; if( tier == UI_GetNumSPTiers() ) { info = UI_GetSpecialArenaInfo( "training" ); if( levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) { return 0; } info = UI_GetSpecialArenaInfo( "final" ); if( !info || levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) { return tier + 1; } return -1; } for( n = 0; n < ARENAS_PER_TIER; n++, level++ ) { UI_GetBestScore( level, &score, &skill ); if ( score != 1 ) { return -1; } } return tier + 1; } /* =============== UI_ShowTierVideo =============== */ qboolean UI_ShowTierVideo( int tier ) { char key[16]; char videos[MAX_INFO_VALUE]; if( tier <= 0 ) { return qfalse; } trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) ); Com_sprintf( key, sizeof(key), "tier%i", tier ); if( atoi( Info_ValueForKey( videos, key ) ) ) { return qfalse; } Info_SetValueForKey( videos, key, va( "%i", 1 ) ); trap_Cvar_Set( "g_spVideos", videos ); return qtrue; } /* =============== UI_CanShowTierVideo =============== */ qboolean UI_CanShowTierVideo( int tier ) { char key[16]; char videos[MAX_INFO_VALUE]; if( !tier ) { return qfalse; } if( uis.demoversion && tier != 8 ) { return qfalse; } trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) ); Com_sprintf( key, sizeof(key), "tier%i", tier ); if( atoi( Info_ValueForKey( videos, key ) ) ) { return qtrue; } return qfalse; } /* =============== UI_GetCurrentGame Returns the next level the player has not won =============== */ int UI_GetCurrentGame( void ) { int level; int rank; int skill; const char *info; info = UI_GetSpecialArenaInfo( "training" ); if( info ) { level = atoi( Info_ValueForKey( info, "num" ) ); UI_GetBestScore( level, &rank, &skill ); if ( !rank || rank > 1 ) { return level; } } for( level = 0; level < ui_numSinglePlayerArenas; level++ ) { UI_GetBestScore( level, &rank, &skill ); if ( !rank || rank > 1 ) { return level; } } info = UI_GetSpecialArenaInfo( "final" ); if( !info ) { return -1; } return atoi( Info_ValueForKey( info, "num" ) ); } /* =============== UI_NewGame Clears the scores and sets the difficutly level =============== */ void UI_NewGame( void ) { trap_Cvar_Set( "g_spScores1", "" ); trap_Cvar_Set( "g_spScores2", "" ); trap_Cvar_Set( "g_spScores3", "" ); trap_Cvar_Set( "g_spScores4", "" ); trap_Cvar_Set( "g_spScores5", "" ); trap_Cvar_Set( "g_spAwards", "" ); trap_Cvar_Set( "g_spVideos", "" ); } /* =============== UI_GetNumArenas =============== */ int UI_GetNumArenas( void ) { return ui_numArenas; } /* =============== UI_GetNumSPArenas =============== */ int UI_GetNumSPArenas( void ) { return ui_numSinglePlayerArenas; } /* =============== UI_GetNumSPTiers =============== */ int UI_GetNumSPTiers( void ) { return ui_numSinglePlayerArenas / ARENAS_PER_TIER; } /* =============== UI_GetNumBots =============== */ int UI_GetNumBots( void ) { return ui_numBots; } /* =============== UI_SPUnlock_f =============== */ void UI_SPUnlock_f( void ) { char arenaKey[16]; char scores[MAX_INFO_VALUE]; int level; int tier; // get scores for skill 1 trap_Cvar_VariableStringBuffer( "g_spScores1", scores, MAX_INFO_VALUE ); // update scores for( level = 0; level < ui_numSinglePlayerArenas + ui_numSpecialSinglePlayerArenas; level++ ) { Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level ); Info_SetValueForKey( scores, arenaKey, "1" ); } trap_Cvar_Set( "g_spScores1", scores ); // unlock cinematics for( tier = 1; tier <= 8; tier++ ) { UI_ShowTierVideo( tier ); } trap_Print( "All levels unlocked at skill level 1\n" ); UI_SPLevelMenu_ReInit(); } /* =============== UI_SPUnlockMedals_f =============== */ void UI_SPUnlockMedals_f( void ) { int n; char key[16]; char awardData[MAX_INFO_VALUE]; trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, MAX_INFO_VALUE ); for( n = 0; n < 6; n++ ) { Com_sprintf( key, sizeof(key), "a%i", n ); Info_SetValueForKey( awardData, key, "100" ); } trap_Cvar_Set( "g_spAwards", awardData ); trap_Print( "All levels unlocked at 100\n" ); } /* =============== UI_InitGameinfo =============== */ void UI_InitGameinfo( void ) { UI_InitMemory(); UI_LoadArenas(); UI_LoadBots(); uis.demoversion = qfalse; } openarena_0.8.8.orig/code/q3_ui/ui_sound.c0000644000175000017500000002727411656310261017162 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= SOUND OPTIONS MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ID_GRAPHICS 10 #define ID_DISPLAY 11 #define ID_SOUND 12 #define ID_NETWORK 13 #define ID_EFFECTSVOLUME 14 #define ID_MUSICVOLUME 15 #define ID_QUALITY 16 //#define ID_A3D 17 //Sago: Here I do some stuff! #define ID_OPENAL 18 #define ID_BACK 19 static const char *quality_items[] = { "Low", "High", NULL }; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s graphics; menutext_s display; menutext_s sound; menutext_s network; menuslider_s sfxvolume; menuslider_s musicvolume; menulist_s quality; // menuradiobutton_s a3d; menuradiobutton_s openal; menubitmap_s back; } soundOptionsInfo_t; static soundOptionsInfo_t soundOptionsInfo; /* ================= UI_SoundOptionsMenu_Event ================= */ static void UI_SoundOptionsMenu_Event( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GRAPHICS: UI_PopMenu(); UI_GraphicsOptionsMenu(); break; case ID_DISPLAY: UI_PopMenu(); UI_DisplayOptionsMenu(); break; case ID_SOUND: break; case ID_NETWORK: UI_PopMenu(); UI_NetworkOptionsMenu(); break; case ID_EFFECTSVOLUME: trap_Cvar_SetValue( "s_volume", soundOptionsInfo.sfxvolume.curvalue / 10 ); break; case ID_MUSICVOLUME: trap_Cvar_SetValue( "s_musicvolume", soundOptionsInfo.musicvolume.curvalue / 10 ); break; case ID_QUALITY: if( soundOptionsInfo.quality.curvalue ) { trap_Cvar_SetValue( "s_khz", 22 ); trap_Cvar_SetValue( "s_compression", 0 ); } else { trap_Cvar_SetValue( "s_khz", 11 ); trap_Cvar_SetValue( "s_compression", 1 ); } UI_ForceMenuOff(); trap_Cmd_ExecuteText( EXEC_APPEND, "snd_restart\n" ); break; /* case ID_A3D: if( soundOptionsInfo.a3d.curvalue ) { trap_Cmd_ExecuteText( EXEC_NOW, "s_enable_a3d\n" ); } else { trap_Cmd_ExecuteText( EXEC_NOW, "s_disable_a3d\n" ); } soundOptionsInfo.a3d.curvalue = (int)trap_Cvar_VariableValue( "s_usingA3D" ); break; */ case ID_OPENAL: if( soundOptionsInfo.openal.curvalue ) { trap_Cmd_ExecuteText( EXEC_NOW, "s_useopenal 1\n" ); } else { trap_Cmd_ExecuteText( EXEC_NOW, "s_useopenal 0\n" ); } soundOptionsInfo.openal.curvalue = (int)trap_Cvar_VariableValue( "s_useopenal" ); break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_SoundOptionsMenu_Init =============== */ static void UI_SoundOptionsMenu_Init( void ) { int y; memset( &soundOptionsInfo, 0, sizeof(soundOptionsInfo) ); UI_SoundOptionsMenu_Cache(); soundOptionsInfo.menu.wrapAround = qtrue; soundOptionsInfo.menu.fullscreen = qtrue; soundOptionsInfo.banner.generic.type = MTYPE_BTEXT; soundOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY; soundOptionsInfo.banner.generic.x = 320; soundOptionsInfo.banner.generic.y = 16; soundOptionsInfo.banner.string = "SYSTEM SETUP"; soundOptionsInfo.banner.color = color_white; soundOptionsInfo.banner.style = UI_CENTER; soundOptionsInfo.framel.generic.type = MTYPE_BITMAP; soundOptionsInfo.framel.generic.name = ART_FRAMEL; soundOptionsInfo.framel.generic.flags = QMF_INACTIVE; soundOptionsInfo.framel.generic.x = 0; soundOptionsInfo.framel.generic.y = 78; soundOptionsInfo.framel.width = 256; soundOptionsInfo.framel.height = 329; soundOptionsInfo.framer.generic.type = MTYPE_BITMAP; soundOptionsInfo.framer.generic.name = ART_FRAMER; soundOptionsInfo.framer.generic.flags = QMF_INACTIVE; soundOptionsInfo.framer.generic.x = 376; soundOptionsInfo.framer.generic.y = 76; soundOptionsInfo.framer.width = 256; soundOptionsInfo.framer.height = 334; soundOptionsInfo.graphics.generic.type = MTYPE_PTEXT; soundOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; soundOptionsInfo.graphics.generic.id = ID_GRAPHICS; soundOptionsInfo.graphics.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.graphics.generic.x = 216; soundOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT; soundOptionsInfo.graphics.string = "GRAPHICS"; soundOptionsInfo.graphics.style = UI_RIGHT; soundOptionsInfo.graphics.color = color_red; soundOptionsInfo.display.generic.type = MTYPE_PTEXT; soundOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; soundOptionsInfo.display.generic.id = ID_DISPLAY; soundOptionsInfo.display.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.display.generic.x = 216; soundOptionsInfo.display.generic.y = 240 - PROP_HEIGHT; soundOptionsInfo.display.string = "DISPLAY"; soundOptionsInfo.display.style = UI_RIGHT; soundOptionsInfo.display.color = color_red; soundOptionsInfo.sound.generic.type = MTYPE_PTEXT; soundOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY; soundOptionsInfo.sound.generic.id = ID_SOUND; soundOptionsInfo.sound.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.sound.generic.x = 216; soundOptionsInfo.sound.generic.y = 240; soundOptionsInfo.sound.string = "SOUND"; soundOptionsInfo.sound.style = UI_RIGHT; soundOptionsInfo.sound.color = color_red; soundOptionsInfo.network.generic.type = MTYPE_PTEXT; soundOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; soundOptionsInfo.network.generic.id = ID_NETWORK; soundOptionsInfo.network.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.network.generic.x = 216; soundOptionsInfo.network.generic.y = 240 + PROP_HEIGHT; soundOptionsInfo.network.string = "NETWORK"; soundOptionsInfo.network.style = UI_RIGHT; soundOptionsInfo.network.color = color_red; y = 240 - 1.5 * (BIGCHAR_HEIGHT + 2); soundOptionsInfo.sfxvolume.generic.type = MTYPE_SLIDER; soundOptionsInfo.sfxvolume.generic.name = "Effects Volume:"; soundOptionsInfo.sfxvolume.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; soundOptionsInfo.sfxvolume.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.sfxvolume.generic.id = ID_EFFECTSVOLUME; soundOptionsInfo.sfxvolume.generic.x = 400; soundOptionsInfo.sfxvolume.generic.y = y; soundOptionsInfo.sfxvolume.minvalue = 0; soundOptionsInfo.sfxvolume.maxvalue = 10; y += BIGCHAR_HEIGHT+2; soundOptionsInfo.musicvolume.generic.type = MTYPE_SLIDER; soundOptionsInfo.musicvolume.generic.name = "Music Volume:"; soundOptionsInfo.musicvolume.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; soundOptionsInfo.musicvolume.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.musicvolume.generic.id = ID_MUSICVOLUME; soundOptionsInfo.musicvolume.generic.x = 400; soundOptionsInfo.musicvolume.generic.y = y; soundOptionsInfo.musicvolume.minvalue = 0; soundOptionsInfo.musicvolume.maxvalue = 10; y += BIGCHAR_HEIGHT+2; soundOptionsInfo.quality.generic.type = MTYPE_SPINCONTROL; soundOptionsInfo.quality.generic.name = "Sound Quality:"; soundOptionsInfo.quality.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; soundOptionsInfo.quality.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.quality.generic.id = ID_QUALITY; soundOptionsInfo.quality.generic.x = 400; soundOptionsInfo.quality.generic.y = y; soundOptionsInfo.quality.itemnames = quality_items; /* y += BIGCHAR_HEIGHT+2; soundOptionsInfo.a3d.generic.type = MTYPE_RADIOBUTTON; soundOptionsInfo.a3d.generic.name = "A3D:"; soundOptionsInfo.a3d.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; soundOptionsInfo.a3d.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.a3d.generic.id = ID_A3D; soundOptionsInfo.a3d.generic.x = 400; soundOptionsInfo.a3d.generic.y = y; */ y += BIGCHAR_HEIGHT+2; soundOptionsInfo.openal.generic.type = MTYPE_RADIOBUTTON; soundOptionsInfo.openal.generic.name = "OpenAL:"; soundOptionsInfo.openal.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; soundOptionsInfo.openal.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.openal.generic.id = ID_OPENAL; soundOptionsInfo.openal.generic.x = 400; soundOptionsInfo.openal.generic.y = y; soundOptionsInfo.back.generic.type = MTYPE_BITMAP; soundOptionsInfo.back.generic.name = ART_BACK0; soundOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; soundOptionsInfo.back.generic.callback = UI_SoundOptionsMenu_Event; soundOptionsInfo.back.generic.id = ID_BACK; soundOptionsInfo.back.generic.x = 0; soundOptionsInfo.back.generic.y = 480-64; soundOptionsInfo.back.width = 128; soundOptionsInfo.back.height = 64; soundOptionsInfo.back.focuspic = ART_BACK1; Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.banner ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.framel ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.framer ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.graphics ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.display ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.sound ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.network ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.sfxvolume ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.musicvolume ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.quality ); // Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.a3d ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.openal ); Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.back ); soundOptionsInfo.sfxvolume.curvalue = trap_Cvar_VariableValue( "s_volume" ) * 10; soundOptionsInfo.musicvolume.curvalue = trap_Cvar_VariableValue( "s_musicvolume" ) * 10; soundOptionsInfo.quality.curvalue = !trap_Cvar_VariableValue( "s_compression" ); // soundOptionsInfo.a3d.curvalue = (int)trap_Cvar_VariableValue( "s_usingA3D" ); soundOptionsInfo.openal.curvalue = (int)trap_Cvar_VariableValue( "s_useopenal" ); } /* =============== UI_SoundOptionsMenu_Cache =============== */ void UI_SoundOptionsMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== UI_SoundOptionsMenu =============== */ void UI_SoundOptionsMenu( void ) { UI_SoundOptionsMenu_Init(); UI_PushMenu( &soundOptionsInfo.menu ); Menu_SetCursorToItem( &soundOptionsInfo.menu, &soundOptionsInfo.sound ); } openarena_0.8.8.orig/code/q3_ui/ui_credits.c0000644000175000017500000000511111656310261017451 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= CREDITS ======================================================================= */ /* *Sago 2008-06-29: This is kinda annoying and does not really give usefull information anyway */ #include "ui_local.h" typedef struct { menuframework_s menu; } creditsmenu_t; static creditsmenu_t s_credits; /* ================= UI_CreditMenu_Key ================= */ static sfxHandle_t UI_CreditMenu_Key( int key ) { if( key & K_CHAR_FLAG ) { return 0; } //Sago: I no longer show credits on close. Consider something else if ingame credits are to be made //trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); return 0; } /* =============== UI_CreditMenu_Draw =============== */ static void UI_CreditMenu_Draw( void ) { int y; y = 12; UI_DrawProportionalString( 320, y, "Thank you for playing", UI_CENTER|UI_SMALLFONT, color_white ); y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE; UI_DrawProportionalString( 320, y, "Open Arena", UI_CENTER|UI_SMALLFONT, color_white ); y += 228; UI_DrawString( 320, y, "Terminating...", UI_CENTER|UI_SMALLFONT, color_red ); y = 480 - PROP_HEIGHT * PROP_SMALL_SIZE_SCALE; UI_DrawProportionalString( 320, y, "www.openarena.ws", UI_CENTER|UI_SMALLFONT, color_white ); } /* =============== UI_CreditMenu =============== */ void UI_CreditMenu( void ) { memset( &s_credits, 0 ,sizeof(s_credits) ); s_credits.menu.draw = UI_CreditMenu_Draw; s_credits.menu.key = UI_CreditMenu_Key; s_credits.menu.fullscreen = qtrue; UI_PushMenu ( &s_credits.menu ); trap_Cmd_ExecuteText( EXEC_APPEND, "wait 2; quit\n" ); } openarena_0.8.8.orig/code/q3_ui/ui_options.c0000644000175000017500000001474711656310261017526 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* ======================================================================= SYSTEM CONFIGURATION MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ID_GRAPHICS 10 #define ID_DISPLAY 11 #define ID_SOUND 12 #define ID_NETWORK 13 #define ID_BACK 14 #define VERTICAL_SPACING 34 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s graphics; menutext_s display; menutext_s sound; menutext_s network; menubitmap_s back; } optionsmenu_t; static optionsmenu_t s_options; /* ================= Options_Event ================= */ static void Options_Event( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GRAPHICS: UI_GraphicsOptionsMenu(); break; case ID_DISPLAY: UI_DisplayOptionsMenu(); break; case ID_SOUND: UI_SoundOptionsMenu(); break; case ID_NETWORK: UI_NetworkOptionsMenu(); break; case ID_BACK: UI_PopMenu(); break; } } /* =============== SystemConfig_Cache =============== */ void SystemConfig_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== Options_MenuInit =============== */ void Options_MenuInit( void ) { int y; uiClientState_t cstate; memset( &s_options, 0, sizeof(optionsmenu_t) ); SystemConfig_Cache(); s_options.menu.wrapAround = qtrue; trap_GetClientState( &cstate ); if ( cstate.connState >= CA_CONNECTED ) { s_options.menu.fullscreen = qfalse; } else { s_options.menu.fullscreen = qtrue; } s_options.banner.generic.type = MTYPE_BTEXT; s_options.banner.generic.flags = QMF_CENTER_JUSTIFY; s_options.banner.generic.x = 320; s_options.banner.generic.y = 16; s_options.banner.string = "SYSTEM SETUP"; s_options.banner.color = color_white; s_options.banner.style = UI_CENTER; s_options.framel.generic.type = MTYPE_BITMAP; s_options.framel.generic.name = ART_FRAMEL; s_options.framel.generic.flags = QMF_INACTIVE; s_options.framel.generic.x = 8; s_options.framel.generic.y = 76; s_options.framel.width = 256; s_options.framel.height = 334; s_options.framer.generic.type = MTYPE_BITMAP; s_options.framer.generic.name = ART_FRAMER; s_options.framer.generic.flags = QMF_INACTIVE; s_options.framer.generic.x = 376; s_options.framer.generic.y = 76; s_options.framer.width = 256; s_options.framer.height = 334; y = 168; s_options.graphics.generic.type = MTYPE_PTEXT; s_options.graphics.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_options.graphics.generic.callback = Options_Event; s_options.graphics.generic.id = ID_GRAPHICS; s_options.graphics.generic.x = 320; s_options.graphics.generic.y = y; s_options.graphics.string = "GRAPHICS"; s_options.graphics.color = color_red; s_options.graphics.style = UI_CENTER; y += VERTICAL_SPACING; s_options.display.generic.type = MTYPE_PTEXT; s_options.display.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_options.display.generic.callback = Options_Event; s_options.display.generic.id = ID_DISPLAY; s_options.display.generic.x = 320; s_options.display.generic.y = y; s_options.display.string = "DISPLAY"; s_options.display.color = color_red; s_options.display.style = UI_CENTER; y += VERTICAL_SPACING; s_options.sound.generic.type = MTYPE_PTEXT; s_options.sound.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_options.sound.generic.callback = Options_Event; s_options.sound.generic.id = ID_SOUND; s_options.sound.generic.x = 320; s_options.sound.generic.y = y; s_options.sound.string = "SOUND"; s_options.sound.color = color_red; s_options.sound.style = UI_CENTER; y += VERTICAL_SPACING; s_options.network.generic.type = MTYPE_PTEXT; s_options.network.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_options.network.generic.callback = Options_Event; s_options.network.generic.id = ID_NETWORK; s_options.network.generic.x = 320; s_options.network.generic.y = y; s_options.network.string = "NETWORK"; s_options.network.color = color_red; s_options.network.style = UI_CENTER; s_options.back.generic.type = MTYPE_BITMAP; s_options.back.generic.name = ART_BACK0; s_options.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_options.back.generic.callback = Options_Event; s_options.back.generic.id = ID_BACK; s_options.back.generic.x = 0; s_options.back.generic.y = 480-64; s_options.back.width = 128; s_options.back.height = 64; s_options.back.focuspic = ART_BACK1; Menu_AddItem( &s_options.menu, ( void * ) &s_options.banner ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.framel ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.framer ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.graphics ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.display ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.sound ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.network ); Menu_AddItem( &s_options.menu, ( void * ) &s_options.back ); } /* =============== UI_SystemConfigMenu =============== */ void UI_SystemConfigMenu( void ) { Options_MenuInit(); UI_PushMenu ( &s_options.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_specifyserver.c0000644000175000017500000001513511656310261020714 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /********************************************************************************* SPECIFY SERVER *********************************************************************************/ #define SPECIFYSERVER_FRAMEL "menu/art_blueish/frame2_l" #define SPECIFYSERVER_FRAMER "menu/art_blueish/frame1_r" #define SPECIFYSERVER_BACK0 "menu/art_blueish/back_0" #define SPECIFYSERVER_BACK1 "menu/art_blueish/back_1" #define SPECIFYSERVER_FIGHT0 "menu/art_blueish/fight_0" #define SPECIFYSERVER_FIGHT1 "menu/art_blueish/fight_1" #define ID_SPECIFYSERVERBACK 102 #define ID_SPECIFYSERVERGO 103 static char* specifyserver_artlist[] = { SPECIFYSERVER_FRAMEL, SPECIFYSERVER_FRAMER, SPECIFYSERVER_BACK0, SPECIFYSERVER_BACK1, SPECIFYSERVER_FIGHT0, SPECIFYSERVER_FIGHT1, NULL }; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menufield_s domain; menufield_s port; menubitmap_s go; menubitmap_s back; } specifyserver_t; static specifyserver_t s_specifyserver; /* ================= SpecifyServer_Event ================= */ static void SpecifyServer_Event( void* ptr, int event ) { char buff[256]; switch (((menucommon_s*)ptr)->id) { case ID_SPECIFYSERVERGO: if (event != QM_ACTIVATED) break; if (s_specifyserver.domain.field.buffer[0]) { strcpy(buff,s_specifyserver.domain.field.buffer); if (s_specifyserver.port.field.buffer[0]) Com_sprintf( buff+strlen(buff), 128, ":%s", s_specifyserver.port.field.buffer ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) ); } break; case ID_SPECIFYSERVERBACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; } } /* ================= SpecifyServer_MenuInit ================= */ void SpecifyServer_MenuInit( void ) { // zero set all our globals memset( &s_specifyserver, 0 ,sizeof(specifyserver_t) ); SpecifyServer_Cache(); s_specifyserver.menu.wrapAround = qtrue; s_specifyserver.menu.fullscreen = qtrue; s_specifyserver.banner.generic.type = MTYPE_BTEXT; s_specifyserver.banner.generic.x = 320; s_specifyserver.banner.generic.y = 16; s_specifyserver.banner.string = "SPECIFY SERVER"; s_specifyserver.banner.color = color_white; s_specifyserver.banner.style = UI_CENTER; s_specifyserver.framel.generic.type = MTYPE_BITMAP; s_specifyserver.framel.generic.name = SPECIFYSERVER_FRAMEL; s_specifyserver.framel.generic.flags = QMF_INACTIVE; s_specifyserver.framel.generic.x = 0; s_specifyserver.framel.generic.y = 78; s_specifyserver.framel.width = 256; s_specifyserver.framel.height = 329; s_specifyserver.framer.generic.type = MTYPE_BITMAP; s_specifyserver.framer.generic.name = SPECIFYSERVER_FRAMER; s_specifyserver.framer.generic.flags = QMF_INACTIVE; s_specifyserver.framer.generic.x = 376; s_specifyserver.framer.generic.y = 76; s_specifyserver.framer.width = 256; s_specifyserver.framer.height = 334; s_specifyserver.domain.generic.type = MTYPE_FIELD; s_specifyserver.domain.generic.name = "Address:"; s_specifyserver.domain.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_specifyserver.domain.generic.x = 206; s_specifyserver.domain.generic.y = 220; s_specifyserver.domain.field.widthInChars = 38; s_specifyserver.domain.field.maxchars = 80; s_specifyserver.port.generic.type = MTYPE_FIELD; s_specifyserver.port.generic.name = "Port:"; s_specifyserver.port.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT|QMF_NUMBERSONLY; s_specifyserver.port.generic.x = 206; s_specifyserver.port.generic.y = 250; s_specifyserver.port.field.widthInChars = 6; s_specifyserver.port.field.maxchars = 5; s_specifyserver.go.generic.type = MTYPE_BITMAP; s_specifyserver.go.generic.name = SPECIFYSERVER_FIGHT0; s_specifyserver.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_specifyserver.go.generic.callback = SpecifyServer_Event; s_specifyserver.go.generic.id = ID_SPECIFYSERVERGO; s_specifyserver.go.generic.x = 640; s_specifyserver.go.generic.y = 480-64; s_specifyserver.go.width = 128; s_specifyserver.go.height = 64; s_specifyserver.go.focuspic = SPECIFYSERVER_FIGHT1; s_specifyserver.back.generic.type = MTYPE_BITMAP; s_specifyserver.back.generic.name = SPECIFYSERVER_BACK0; s_specifyserver.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_specifyserver.back.generic.callback = SpecifyServer_Event; s_specifyserver.back.generic.id = ID_SPECIFYSERVERBACK; s_specifyserver.back.generic.x = 0; s_specifyserver.back.generic.y = 480-64; s_specifyserver.back.width = 128; s_specifyserver.back.height = 64; s_specifyserver.back.focuspic = SPECIFYSERVER_BACK1; Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.banner ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.framel ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.framer ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.domain ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.port ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.go ); Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.back ); Com_sprintf( s_specifyserver.port.field.buffer, 6, "%i", 27960 ); } /* ================= SpecifyServer_Cache ================= */ void SpecifyServer_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!specifyserver_artlist[i]) break; trap_R_RegisterShaderNoMip(specifyserver_artlist[i]); } } /* ================= UI_SpecifyServerMenu ================= */ void UI_SpecifyServerMenu( void ) { SpecifyServer_MenuInit(); UI_PushMenu( &s_specifyserver.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_atoms.c0000644000175000017500000006350711656310261017154 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /********************************************************************** UI_ATOMS.C User interface building blocks and support functions. **********************************************************************/ #include "ui_local.h" uiStatic_t uis; qboolean m_entersound; // after a frame, so caching won't disrupt the sound void QDECL Com_Error( int level, const char *error, ... ) { va_list argptr; char text[1024]; va_start (argptr, error); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); trap_Error( va("%s", text) ); } void QDECL Com_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); trap_Print( va("%s", text) ); } /* ================= UI_ClampCvar ================= */ float UI_ClampCvar( float min, float max, float value ) { if ( value < min ) return min; if ( value > max ) return max; return value; } /* ================= UI_StartDemoLoop ================= */ void UI_StartDemoLoop( void ) { trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" ); } /* ================= UI_PushMenu ================= */ void UI_PushMenu( menuframework_s *menu ) { int i; menucommon_s* item; // avoid stacking menus invoked by hotkeys for (i=0 ; i= MAX_MENUDEPTH) trap_Error("UI_PushMenu: menu stack overflow"); uis.stack[uis.menusp++] = menu; } uis.activemenu = menu; // default cursor position menu->cursor = 0; menu->cursor_prev = 0; m_entersound = qtrue; trap_Key_SetCatcher( KEYCATCH_UI ); // force first available item to have focus for (i=0; initems; i++) { item = (menucommon_s *)menu->items[i]; if (!(item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE))) { menu->cursor_prev = -1; Menu_SetCursor( menu, i ); break; } } uis.firstdraw = qtrue; } /* ================= UI_PopMenu ================= */ void UI_PopMenu (void) { trap_S_StartLocalSound( menu_out_sound, CHAN_LOCAL_SOUND ); uis.menusp--; if (uis.menusp < 0) trap_Error ("UI_PopMenu: menu stack underflow"); if (uis.menusp) { uis.activemenu = uis.stack[uis.menusp-1]; uis.firstdraw = qtrue; } else { UI_ForceMenuOff (); } } void UI_ForceMenuOff (void) { uis.menusp = 0; uis.activemenu = NULL; trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); trap_Key_ClearStates(); trap_Cvar_Set( "cl_paused", "0" ); } /* ================= UI_LerpColor ================= */ void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t) { int i; // lerp and clamp each component for (i=0; i<4; i++) { c[i] = a[i] + t*(b[i]-a[i]); if (c[i] < 0) c[i] = 0; else if (c[i] > 1.0) c[i] = 1.0; } } /* ================= UI_DrawProportionalString2 ================= */ static int propMap[128][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, PROP_SPACE_WIDTH}, // SPACE {11, 122, 7}, // ! {154, 181, 14}, // " {55, 122, 17}, // # {79, 122, 18}, // $ {101, 122, 23}, // % {153, 122, 18}, // & {9, 93, 7}, // ' {207, 122, 8}, // ( {230, 122, 9}, // ) {177, 122, 18}, // * {30, 152, 18}, // + {85, 181, 7}, // , {34, 93, 11}, // - {110, 181, 6}, // . {130, 152, 14}, // / {22, 64, 17}, // 0 {41, 64, 12}, // 1 {58, 64, 17}, // 2 {78, 64, 18}, // 3 {98, 64, 19}, // 4 {120, 64, 18}, // 5 {141, 64, 18}, // 6 {204, 64, 16}, // 7 {162, 64, 17}, // 8 {182, 64, 18}, // 9 {59, 181, 7}, // : {35,181, 7}, // ; {203, 152, 14}, // < {56, 93, 14}, // = {228, 152, 14}, // > {177, 181, 18}, // ? {28, 122, 22}, // @ {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {60, 152, 7}, // [ {106, 151, 13}, // '\' {83, 152, 7}, // ] {128, 122, 17}, // ^ {4, 152, 21}, // _ {134, 181, 5}, // ' {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {153, 152, 13}, // { {11, 181, 5}, // | {180, 152, 13}, // } {79, 93, 17}, // ~ {0, 0, -1} // DEL }; static int propMapB[26][3] = { {11, 12, 33}, {49, 12, 31}, {85, 12, 31}, {120, 12, 30}, {156, 12, 21}, {183, 12, 21}, {207, 12, 32}, {13, 55, 30}, {49, 55, 13}, {66, 55, 29}, {101, 55, 31}, {135, 55, 21}, {158, 55, 40}, {204, 55, 32}, {12, 97, 31}, {48, 97, 31}, {82, 97, 30}, {118, 97, 30}, {153, 97, 30}, {185, 97, 25}, {213, 97, 30}, {11, 139, 32}, {42, 139, 51}, {93, 139, 32}, {126, 139, 31}, {158, 139, 25}, }; #define PROPB_GAP_WIDTH 4 #define PROPB_SPACE_WIDTH 12 #define PROPB_HEIGHT 36 // bk001205 - code below duplicated in cgame/cg_drawtools.c // bk001205 - FIXME: does this belong in ui_shared.c? /* ================= UI_DrawBannerString ================= */ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color ) { const char* s; unsigned char ch; // bk001204 - unsigned float ax; float ay; float aw; float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * uis.xscale + uis.bias; ay = y * uis.yscale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* uis.xscale; } else if ( ch >= 'A' && ch <= 'Z' ) { ch -= 'A'; fcol = (float)propMapB[ch][0] / 256.0f; frow = (float)propMapB[ch][1] / 256.0f; fwidth = (float)propMapB[ch][2] / 256.0f; fheight = (float)PROPB_HEIGHT / 256.0f; aw = (float)propMapB[ch][2] * uis.xscale; ah = (float)PROPB_HEIGHT * uis.yscale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, uis.charsetPropB ); ax += (aw + (float)PROPB_GAP_WIDTH * uis.xscale); } s++; } trap_R_SetColor( NULL ); } void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) { const char * s; int ch; int width; vec4_t drawcolor; // find the width of the drawn text s = str; width = 0; while ( *s ) { ch = *s; if ( ch == ' ' ) { width += PROPB_SPACE_WIDTH; } else if ( ch >= 'A' && ch <= 'Z' ) { width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH; } s++; } width -= PROPB_GAP_WIDTH; switch( style & UI_FORMATMASK ) { case UI_CENTER: x -= width / 2; break; case UI_RIGHT: x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawBannerString2( x+2, y+2, str, drawcolor ); } UI_DrawBannerString2( x, y, str, color ); } int UI_ProportionalStringWidth( const char* str ) { const char * s; int ch; int charWidth; int width; s = str; width = 0; while ( *s ) { ch = *s & 127; charWidth = propMap[ch][2]; if ( charWidth != -1 ) { width += charWidth; width += PROP_GAP_WIDTH; } s++; } width -= PROP_GAP_WIDTH; return width; } static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset ) { const char* s; unsigned char ch; // bk001204 - unsigned float ax; float ay; float aw = 0; // bk001204 - init float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * uis.xscale + uis.bias; ay = y * uis.yscale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { aw = (float)PROP_SPACE_WIDTH * uis.xscale * sizeScale; } else if ( propMap[ch][2] != -1 ) { fcol = (float)propMap[ch][0] / 256.0f; frow = (float)propMap[ch][1] / 256.0f; fwidth = (float)propMap[ch][2] / 256.0f; fheight = (float)PROP_HEIGHT / 256.0f; aw = (float)propMap[ch][2] * uis.xscale * sizeScale; ah = (float)PROP_HEIGHT * uis.yscale * sizeScale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset ); } ax += (aw + (float)PROP_GAP_WIDTH * uis.xscale * sizeScale); s++; } trap_R_SetColor( NULL ); } /* ================= UI_ProportionalSizeScale ================= */ float UI_ProportionalSizeScale( int style ) { if( style & UI_SMALLFONT ) { return PROP_SMALL_SIZE_SCALE; } return 1.00; } /* ================= UI_DrawProportionalString ================= */ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) { vec4_t drawcolor; int width; float sizeScale; sizeScale = UI_ProportionalSizeScale( style ); switch( style & UI_FORMATMASK ) { case UI_CENTER: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width / 2; break; case UI_RIGHT: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, uis.charsetProp ); } if ( style & UI_INVERSE ) { drawcolor[0] = color[0] * 0.7; drawcolor[1] = color[1] * 0.7; drawcolor[2] = color[2] * 0.7; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetProp ); return; } if ( style & UI_PULSE ) { drawcolor[0] = color[0] * 0.7; drawcolor[1] = color[1] * 0.7; drawcolor[2] = color[2] * 0.7; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp ); drawcolor[0] = color[0]; drawcolor[1] = color[1]; drawcolor[2] = color[2]; drawcolor[3] = 0.5 + 0.5 * sin( uis.realtime / PULSE_DIVISOR ); UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetPropGlow ); return; } UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp ); } /* ================= UI_DrawProportionalString_Wrapped ================= */ void UI_DrawProportionalString_AutoWrapped( int x, int y, int xmax, int ystep, const char* str, int style, vec4_t color ) { int width; char *s1,*s2,*s3; char c_bcp; char buf[1024]; float sizeScale; if (!str || str[0]=='\0') return; sizeScale = UI_ProportionalSizeScale( style ); Q_strncpyz(buf, str, sizeof(buf)); s1 = s2 = s3 = buf; while (1) { do { s3++; } while (*s3!=' ' && *s3!='\0'); c_bcp = *s3; *s3 = '\0'; width = UI_ProportionalStringWidth(s1) * sizeScale; *s3 = c_bcp; if (width > xmax) { if (s1==s2) { // fuck, don't have a clean cut, we'll overflow s2 = s3; } *s2 = '\0'; UI_DrawProportionalString(x, y, s1, style, color); y += ystep; if (c_bcp == '\0') { // that was the last word // we could start a new loop, but that wouldn't be much use // even if the word is too long, we would overflow it (see above) // so just print it now if needed s2++; if (*s2 != '\0') // if we are printing an overflowing line we have s2 == s3 UI_DrawProportionalString(x, y, s2, style, color); break; } s2++; s1 = s2; s3 = s2; } else { s2 = s3; if (c_bcp == '\0') // we reached the end { UI_DrawProportionalString(x, y, s1, style, color); break; } } } } /* ================= UI_DrawString2 ================= */ static void UI_DrawString2( int x, int y, const char* str, vec4_t color, int charw, int charh ) { const char* s; char ch; int forceColor = qfalse; //APSFIXME; vec4_t tempcolor; float ax; float ay; float aw; float ah; float frow; float fcol; if (y < -charh) // offscreen return; // draw the colored text trap_R_SetColor( color ); ax = x * uis.xscale + uis.bias; ay = y * uis.yscale; aw = charw * uis.xscale; ah = charh * uis.yscale; s = str; while ( *s ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { memcpy( tempcolor, g_color_table[ColorIndex(s[1])], sizeof( tempcolor ) ); tempcolor[3] = color[3]; trap_R_SetColor( tempcolor ); } s += 2; continue; } ch = *s & 255; if (ch != ' ') { frow = (ch>>4)*0.0625; fcol = (ch&15)*0.0625; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + 0.0625, frow + 0.0625, uis.charset ); } ax += aw; s++; } trap_R_SetColor( NULL ); } /* ================= UI_DrawString ================= */ void UI_DrawString( int x, int y, const char* str, int style, vec4_t color ) { int len; int charw; int charh; vec4_t newcolor; vec4_t lowlight; float *drawcolor; vec4_t dropcolor; if( !str ) { return; } if ((style & UI_BLINK) && ((uis.realtime/BLINK_DIVISOR) & 1)) return; if (style & UI_SMALLFONT) { charw = SMALLCHAR_WIDTH; charh = SMALLCHAR_HEIGHT; } else if (style & UI_GIANTFONT) { charw = GIANTCHAR_WIDTH; charh = GIANTCHAR_HEIGHT; } else { charw = BIGCHAR_WIDTH; charh = BIGCHAR_HEIGHT; } if (style & UI_PULSE) { lowlight[0] = 0.8*color[0]; lowlight[1] = 0.8*color[1]; lowlight[2] = 0.8*color[2]; lowlight[3] = 0.8*color[3]; UI_LerpColor(color,lowlight,newcolor,0.5+0.5*sin(uis.realtime/PULSE_DIVISOR)); drawcolor = newcolor; } else drawcolor = color; switch (style & UI_FORMATMASK) { case UI_CENTER: // center justify at x len = strlen(str); x = x - len*charw/2; break; case UI_RIGHT: // right justify at x len = strlen(str); x = x - len*charw; break; default: // left justify at x break; } if ( style & UI_DROPSHADOW ) { dropcolor[0] = dropcolor[1] = dropcolor[2] = 0; dropcolor[3] = drawcolor[3]; UI_DrawString2(x+2,y+2,str,dropcolor,charw,charh); } UI_DrawString2(x,y,str,drawcolor,charw,charh); } /* ================= UI_DrawChar ================= */ void UI_DrawChar( int x, int y, int ch, int style, vec4_t color ) { char buff[2]; buff[0] = ch; buff[1] = '\0'; UI_DrawString( x, y, buff, style, color ); } qboolean UI_IsFullscreen( void ) { if ( uis.activemenu && ( trap_Key_GetCatcher() & KEYCATCH_UI ) ) { return uis.activemenu->fullscreen; } return qfalse; } static void NeedCDAction( qboolean result ) { if ( !result ) { trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } } static void NeedCDKeyAction( qboolean result ) { if ( !result ) { trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } } void UI_SetActiveMenu( uiMenuCommand_t menu ) { // this should be the ONLY way the menu system is brought up // enusure minumum menu data is cached Menu_Cache(); switch ( menu ) { case UIMENU_NONE: UI_ForceMenuOff(); return; case UIMENU_MAIN: UI_MainMenu(); return; case UIMENU_NEED_CD: UI_ConfirmMenu( "Insert the CD", 0, NeedCDAction ); return; case UIMENU_BAD_CD_KEY: UI_ConfirmMenu( "Bad CD Key", 0, NeedCDKeyAction ); return; case UIMENU_INGAME: /* //GRank UI_RankingsMenu(); return; */ trap_Cvar_Set( "cl_paused", "1" ); UI_InGameMenu(); return; // bk001204 case UIMENU_TEAM: case UIMENU_POSTGAME: default: #ifndef NDEBUG Com_Printf("UI_SetActiveMenu: bad enum %d\n", menu ); #endif break; } } /* ================= UI_KeyEvent ================= */ void UI_KeyEvent( int key, int down ) { sfxHandle_t s; if (!uis.activemenu) { return; } if (!down) { return; } if (uis.activemenu->key) s = uis.activemenu->key( key ); else s = Menu_DefaultKey( uis.activemenu, key ); if ((s > 0) && (s != menu_null_sound)) trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND ); } /* ================= UI_MouseEvent ================= */ void UI_MouseEvent( int dx, int dy ) { int i; menucommon_s* m; if (!uis.activemenu) return; // update mouse screen position uis.cursorx += dx; if (uis.cursorx < -uis.bias) uis.cursorx = -uis.bias; else if (uis.cursorx > SCREEN_WIDTH+uis.bias) uis.cursorx = SCREEN_WIDTH+uis.bias; uis.cursory += dy; if (uis.cursory < 0) uis.cursory = 0; else if (uis.cursory > SCREEN_HEIGHT) uis.cursory = SCREEN_HEIGHT; // region test the active menu items for (i=0; initems; i++) { m = (menucommon_s*)uis.activemenu->items[i]; if (m->flags & (QMF_GRAYED|QMF_INACTIVE)) continue; if ((uis.cursorx < m->left) || (uis.cursorx > m->right) || (uis.cursory < m->top) || (uis.cursory > m->bottom)) { // cursor out of item bounds continue; } // set focus to item at cursor if (uis.activemenu->cursor != i) { Menu_SetCursor( uis.activemenu, i ); ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor_prev]))->flags &= ~QMF_HASMOUSEFOCUS; if ( !(((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags & QMF_SILENT ) ) { trap_S_StartLocalSound( menu_move_sound, CHAN_LOCAL_SOUND ); } } ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags |= QMF_HASMOUSEFOCUS; return; } if (uis.activemenu->nitems > 0) { // out of any region ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags &= ~QMF_HASMOUSEFOCUS; } } char *UI_Argv( int arg ) { static char buffer[MAX_STRING_CHARS]; trap_Argv( arg, buffer, sizeof( buffer ) ); return buffer; } char *UI_Cvar_VariableString( const char *var_name ) { static char buffer[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( var_name, buffer, sizeof( buffer ) ); return buffer; } /* ================= UI_Cache ================= */ void UI_Cache_f( void ) { MainMenu_Cache(); InGame_Cache(); ConfirmMenu_Cache(); PlayerModel_Cache(); PlayerSettings_Cache(); Controls_Cache(); Demos_Cache(); UI_CinematicsMenu_Cache(); Preferences_Cache(); ServerInfo_Cache(); SpecifyServer_Cache(); ArenaServers_Cache(); StartServer_Cache(); ServerOptions_Cache(); DriverInfo_Cache(); GraphicsOptions_Cache(); UI_DisplayOptionsMenu_Cache(); UI_SoundOptionsMenu_Cache(); UI_NetworkOptionsMenu_Cache(); UI_SPLevelMenu_Cache(); UI_SPSkillMenu_Cache(); UI_SPPostgameMenu_Cache(); TeamMain_Cache(); UI_AddBots_Cache(); UI_RemoveBots_Cache(); UI_SetupMenu_Cache(); // UI_LoadConfig_Cache(); // UI_SaveConfigMenu_Cache(); UI_BotSelectMenu_Cache(); UI_CDKeyMenu_Cache(); UI_ModsMenu_Cache(); } /* ================= UI_ConsoleCommand ================= */ qboolean UI_ConsoleCommand( int realTime ) { char *cmd; uis.frametime = realTime - uis.realtime; uis.realtime = realTime; cmd = UI_Argv( 0 ); // ensure minimum menu data is available Menu_Cache(); if ( Q_stricmp (cmd, "levelselect") == 0 ) { UI_SPLevelMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "postgame") == 0 ) { UI_SPPostgameMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_cache") == 0 ) { UI_Cache_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_cinematics") == 0 ) { UI_CinematicsMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) { UI_TeamOrdersMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "iamacheater") == 0 ) { UI_SPUnlock_f(); return qtrue; } if ( Q_stricmp (cmd, "iamamonkey") == 0 ) { UI_SPUnlockMedals_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) { UI_CDKeyMenu_f(); return qtrue; } if ( Q_stricmp (cmd, "ui_mappage") == 0 ) { mappage.pagenumber = atoi(UI_Argv( 1 )); Q_strncpyz(mappage.mapname[0],UI_Argv(2),32); Q_strncpyz(mappage.mapname[1],UI_Argv(3),32); Q_strncpyz(mappage.mapname[2],UI_Argv(4),32); Q_strncpyz(mappage.mapname[3],UI_Argv(5),32); Q_strncpyz(mappage.mapname[4],UI_Argv(6),32); Q_strncpyz(mappage.mapname[5],UI_Argv(7),32); Q_strncpyz(mappage.mapname[6],UI_Argv(8),32); Q_strncpyz(mappage.mapname[7],UI_Argv(9),32); Q_strncpyz(mappage.mapname[8],UI_Argv(10),32); Q_strncpyz(mappage.mapname[9],UI_Argv(11),32); UI_VoteMapMenuInternal(); return qtrue; } return qfalse; } /* ================= UI_Shutdown ================= */ void UI_Shutdown( void ) { } /* ================= UI_Init ================= */ void UI_Init( void ) { UI_RegisterCvars(); UI_InitGameinfo(); // cache redundant calulations trap_GetGlconfig( &uis.glconfig ); // for 640x480 virtualized screen uis.xscale = uis.glconfig.vidWidth * (1.0/640.0); uis.yscale = uis.glconfig.vidHeight * (1.0/480.0); if ( uis.glconfig.vidWidth * 480 > uis.glconfig.vidHeight * 640 ) { // wide screen uis.bias = 0.5 * ( uis.glconfig.vidWidth - ( uis.glconfig.vidHeight * (640.0/480.0) ) ); uis.xscale = uis.yscale; } else { // no wide screen uis.bias = 0; } // initialize the menu system Menu_Cache(); uis.activemenu = NULL; uis.menusp = 0; } /* ================ UI_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) { // expect valid pointers *x = *x * uis.xscale + uis.bias; *y *= uis.yscale; *w *= uis.xscale; *h *= uis.yscale; } void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { qhandle_t hShader; hShader = trap_R_RegisterShaderNoMip( picname ); UI_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) { float s0; float s1; float t0; float t1; if( w < 0 ) { // flip about vertical w = -w; s0 = 1; s1 = 0; } else { s0 = 0; s1 = 1; } if( h < 0 ) { // flip about horizontal h = -h; t0 = 1; t1 = 0; } else { t0 = 0; t1 = 1; } UI_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader ); } /* ================ UI_FillRect Coordinates are 640*480 virtual values ================= */ void UI_FillRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); UI_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uis.whiteShader ); trap_R_SetColor( NULL ); } /* ================ UI_DrawRect Coordinates are 640*480 virtual values ================= */ void UI_DrawRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); UI_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, 1, 0, 0, 0, 0, uis.whiteShader ); trap_R_DrawStretchPic( x, y, 1, height, 0, 0, 0, 0, uis.whiteShader ); trap_R_DrawStretchPic( x, y + height - 1, width, 1, 0, 0, 0, 0, uis.whiteShader ); trap_R_DrawStretchPic( x + width - 1, y, 1, height, 0, 0, 0, 0, uis.whiteShader ); trap_R_SetColor( NULL ); } void UI_SetColor( const float *rgba ) { trap_R_SetColor( rgba ); } void UI_UpdateScreen( void ) { trap_UpdateScreen(); } /* ================= UI_Refresh ================= */ void UI_Refresh( int realtime ) { uis.frametime = realtime - uis.realtime; uis.realtime = realtime; if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) { return; } UI_UpdateCvars(); if ( uis.activemenu ) { if (uis.activemenu->fullscreen) { // draw the background if( uis.activemenu->showlogo ) { UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader ); } else { UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackNoLogoShader ); } } if (uis.activemenu->draw) uis.activemenu->draw(); else Menu_Draw( uis.activemenu ); if( uis.firstdraw ) { UI_MouseEvent( 0, 0 ); uis.firstdraw = qfalse; } } // draw cursor UI_SetColor( NULL ); UI_DrawHandlePic( uis.cursorx-16, uis.cursory-16, 32, 32, uis.cursor); #ifndef NDEBUG if (uis.debug) { // cursor coordinates UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed ); } #endif // delay playing the enter sound until after the // menu has been drawn, to avoid delay while // caching images if (m_entersound) { trap_S_StartLocalSound( menu_in_sound, CHAN_LOCAL_SOUND ); m_entersound = qfalse; } } void UI_DrawTextBox (int x, int y, int width, int lines) { UI_FillRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack ); UI_DrawRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite ); } qboolean UI_CursorInRect (int x, int y, int width, int height) { if (uis.cursorx < x || uis.cursory < y || uis.cursorx > x+width || uis.cursory > y+height) return qfalse; return qtrue; } openarena_0.8.8.orig/code/q3_ui/ui_login.c0000644000175000017500000001316611656310261017135 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // ui_login.c // #include "ui_local.h" #define LOGIN_FRAME "menu/art_blueish/cut_frame" #define ID_NAME 100 #define ID_NAME_BOX 101 #define ID_PASSWORD 102 #define ID_PASSWORD_BOX 103 #define ID_LOGIN 104 #define ID_CANCEL 105 typedef struct { menuframework_s menu; menubitmap_s frame; menutext_s name; menufield_s name_box; menutext_s password; menufield_s password_box; menutext_s login; menutext_s cancel; } login_t; static login_t s_login; static menuframework_s s_login_menu; static menuaction_s s_login_login; static menuaction_s s_login_cancel; static vec4_t s_login_color_prompt = {1.00, 0.43, 0.00, 1.00}; /* =============== Login_MenuEvent =============== */ static void Login_MenuEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_LOGIN: // set name `` //trap_Cvar_Set( "name", s_login.name_box.field.buffer ); /* trap_Cvar_Set( "rank_name", s_login.name_box.field.buffer ); trap_Cvar_Set( "rank_pwd", s_login.password_box.field.buffer ); */ // login trap_CL_UI_RankUserLogin( s_login.name_box.field.buffer, s_login.password_box.field.buffer ); UI_ForceMenuOff(); break; case ID_CANCEL: UI_PopMenu(); break; } } /* =============== Login_MenuInit =============== */ void Login_MenuInit( void ) { int y; memset( &s_login, 0, sizeof(s_login) ); Login_Cache(); s_login.menu.wrapAround = qtrue; s_login.menu.fullscreen = qfalse; s_login.frame.generic.type = MTYPE_BITMAP; s_login.frame.generic.flags = QMF_INACTIVE; s_login.frame.generic.name = LOGIN_FRAME; s_login.frame.generic.x = 142; //320-233; s_login.frame.generic.y = 118; //240-166; s_login.frame.width = 359; //466; s_login.frame.height = 256; //332; y = 214; s_login.name.generic.type = MTYPE_PTEXT; s_login.name.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_login.name.generic.id = ID_NAME; s_login.name.generic.x = 310; s_login.name.generic.y = y; s_login.name.string = "NAME"; s_login.name.style = UI_RIGHT|UI_SMALLFONT; s_login.name.color = s_login_color_prompt; s_login.name_box.generic.type = MTYPE_FIELD; s_login.name_box.generic.ownerdraw = Rankings_DrawName; s_login.name_box.generic.name = ""; s_login.name_box.generic.flags = 0; s_login.name_box.generic.x = 330; s_login.name_box.generic.y = y; s_login.name_box.field.widthInChars = 16; s_login.name_box.field.maxchars = 16; y += 20; s_login.password.generic.type = MTYPE_PTEXT; s_login.password.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; s_login.password.generic.id = ID_PASSWORD; s_login.password.generic.x = 310; s_login.password.generic.y = y; s_login.password.string = "PASSWORD"; s_login.password.style = UI_RIGHT|UI_SMALLFONT; s_login.password.color = s_login_color_prompt; s_login.password_box.generic.type = MTYPE_FIELD; s_login.password_box.generic.ownerdraw = Rankings_DrawPassword; s_login.password_box.generic.name = ""; s_login.password_box.generic.flags = 0; s_login.password_box.generic.x = 330; s_login.password_box.generic.y = y; s_login.password_box.field.widthInChars = 16; s_login.password_box.field.maxchars = 16; y += 40; s_login.login.generic.type = MTYPE_PTEXT; s_login.login.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_login.login.generic.id = ID_LOGIN; s_login.login.generic.callback = Login_MenuEvent; s_login.login.generic.x = 310; s_login.login.generic.y = y; s_login.login.string = "LOGIN"; s_login.login.style = UI_RIGHT|UI_SMALLFONT; s_login.login.color = colorRed; s_login.cancel.generic.type = MTYPE_PTEXT; s_login.cancel.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_login.cancel.generic.id = ID_CANCEL; s_login.cancel.generic.callback = Login_MenuEvent; s_login.cancel.generic.x = 330; s_login.cancel.generic.y = y; s_login.cancel.string = "CANCEL"; s_login.cancel.style = UI_LEFT|UI_SMALLFONT; s_login.cancel.color = colorRed; y += 20; Menu_AddItem( &s_login.menu, (void*) &s_login.frame ); Menu_AddItem( &s_login.menu, (void*) &s_login.name ); Menu_AddItem( &s_login.menu, (void*) &s_login.name_box ); Menu_AddItem( &s_login.menu, (void*) &s_login.password ); Menu_AddItem( &s_login.menu, (void*) &s_login.password_box ); Menu_AddItem( &s_login.menu, (void*) &s_login.login ); Menu_AddItem( &s_login.menu, (void*) &s_login.cancel ); } /* =============== Login_Cache =============== */ void Login_Cache( void ) { trap_R_RegisterShaderNoMip( LOGIN_FRAME ); } /* =============== UI_LoginMenu =============== */ void UI_LoginMenu( void ) { Login_MenuInit(); UI_PushMenu ( &s_login.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_controls2.c0000644000175000017500000015734611656310261017763 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= CONTROLS MENU ======================================================================= */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" typedef struct { char *command; char *label; int id; int anim; int defaultbind1; int defaultbind2; int bind1; int bind2; } bind_t; typedef struct { char* name; float defaultvalue; float value; } configcvar_t; #define SAVE_NOOP 0 #define SAVE_YES 1 #define SAVE_NO 2 #define SAVE_CANCEL 3 // control sections #define C_MOVEMENT 0 #define C_LOOKING 1 #define C_WEAPONS 2 #define C_MISC 3 #define C_MAX 4 #define ID_MOVEMENT 100 #define ID_LOOKING 101 #define ID_WEAPONS 102 #define ID_MISC 103 #define ID_DEFAULTS 104 #define ID_BACK 105 #define ID_SAVEANDEXIT 106 #define ID_EXIT 107 // bindable actions #define ID_SHOWSCORES 0 #define ID_USEITEM 1 #define ID_SPEED 2 #define ID_FORWARD 3 #define ID_BACKPEDAL 4 #define ID_MOVELEFT 5 #define ID_MOVERIGHT 6 #define ID_MOVEUP 7 #define ID_MOVEDOWN 8 #define ID_LEFT 9 #define ID_RIGHT 10 #define ID_STRAFE 11 #define ID_LOOKUP 12 #define ID_LOOKDOWN 13 #define ID_MOUSELOOK 14 #define ID_CENTERVIEW 15 #define ID_ZOOMVIEW 16 #define ID_WEAPON1 17 #define ID_WEAPON2 18 #define ID_WEAPON3 19 #define ID_WEAPON4 20 #define ID_WEAPON5 21 #define ID_WEAPON6 22 #define ID_WEAPON7 23 #define ID_WEAPON8 24 #define ID_WEAPON9 25 #define ID_WEAPON10 26 #define ID_WEAPON11 27 //Nailgun #define ID_WEAPON12 28 //ProxMines #define ID_WEAPON13 29 //Chaingun #define ID_ATTACK 30 #define ID_WEAPPREV 31 #define ID_WEAPNEXT 32 #define ID_GESTURE 33 #define ID_CHAT 34 #define ID_CHAT2 35 #define ID_CHAT3 36 #define ID_CHAT4 37 #define ID_VOIP_TALK 38 // all others #define ID_FREELOOK 39 #define ID_INVERTMOUSE 40 #define ID_ALWAYSRUN 41 #define ID_AUTOSWITCH 42 #define ID_MOUSESPEED 43 #define ID_JOYENABLE 44 #define ID_JOYTHRESHOLD 45 #define ID_SMOOTHMOUSE 46 #define ID_VOIP_TEAMONLY 47 #define ANIM_IDLE 0 #define ANIM_RUN 1 #define ANIM_WALK 2 #define ANIM_BACK 3 #define ANIM_JUMP 4 #define ANIM_CROUCH 5 #define ANIM_STEPLEFT 6 #define ANIM_STEPRIGHT 7 #define ANIM_TURNLEFT 8 #define ANIM_TURNRIGHT 9 #define ANIM_LOOKUP 10 #define ANIM_LOOKDOWN 11 #define ANIM_WEAPON1 12 #define ANIM_WEAPON2 13 #define ANIM_WEAPON3 14 #define ANIM_WEAPON4 15 #define ANIM_WEAPON5 16 #define ANIM_WEAPON6 17 #define ANIM_WEAPON7 18 #define ANIM_WEAPON8 19 #define ANIM_WEAPON9 20 #define ANIM_WEAPON10 21 #define ANIM_ATTACK 22 #define ANIM_GESTURE 23 #define ANIM_DIE 24 #define ANIM_CHAT 25 //New in Beta 23 #define ANIM_WEAPON11 26 //Nailgun #define ANIM_WEAPON12 27 //ProxMines #define ANIM_WEAPON13 28 //Chaingun typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menubitmap_s player; menutext_s movement; menutext_s looking; menutext_s weapons; menutext_s misc; menuaction_s walkforward; menuaction_s backpedal; menuaction_s stepleft; menuaction_s stepright; menuaction_s moveup; menuaction_s movedown; menuaction_s turnleft; menuaction_s turnright; menuaction_s sidestep; menuaction_s run; menuaction_s machinegun; menuaction_s chainsaw; menuaction_s shotgun; menuaction_s grenadelauncher; menuaction_s rocketlauncher; menuaction_s lightning; menuaction_s railgun; menuaction_s plasma; menuaction_s bfg; menuaction_s grapple; //New in Beta 23 menuaction_s nailgun; menuaction_s proxmine; menuaction_s chaingun; //New in 23 end menuaction_s attack; menuaction_s prevweapon; menuaction_s nextweapon; menuaction_s lookup; menuaction_s lookdown; menuaction_s mouselook; menuradiobutton_s freelook; menuaction_s centerview; menuaction_s zoomview; menuaction_s gesture; menuradiobutton_s invertmouse; menuslider_s sensitivity; menuradiobutton_s smoothmouse; menuradiobutton_s alwaysrun; menuaction_s showscores; menulist_s autoswitch; menuaction_s useitem; playerInfo_t playerinfo; qboolean changesmade; menuaction_s chat; menuaction_s chat2; menuaction_s chat3; menuaction_s chat4; menuaction_s voip_talk; menuradiobutton_s voip_teamonly; menuradiobutton_s joyenable; menuslider_s joythreshold; int section; qboolean waitingforkey; char playerModel[64]; vec3_t playerViewangles; vec3_t playerMoveangles; int playerLegs; int playerTorso; int playerWeapon; qboolean playerChat; menubitmap_s back; menutext_s name; } controls_t; static controls_t s_controls; static const char *autoswitch_items[] = { "NEVER", "ALWAYS", "NEW", "BETTER", "NEW&BETTER", NULL }; // OLD //static vec4_t controls_binding_color = {1.00f, 0.43f, 0.00f, 1.00f}; // bk: Win32 C4305 // New! static vec4_t controls_binding_color = {0.58f, 0.70f, 0.81f, 1.00f}; static bind_t g_bindings[] = { {"+scores", "show scores", ID_SHOWSCORES, ANIM_IDLE, K_TAB, -1, -1, -1}, {"+button2", "use item", ID_USEITEM, ANIM_IDLE, K_ENTER, -1, -1, -1}, {"+speed", "run / walk", ID_SPEED, ANIM_RUN, K_SHIFT, -1, -1, -1}, {"+forward", "walk forward", ID_FORWARD, ANIM_WALK, K_UPARROW, -1, -1, -1}, {"+back", "backpedal", ID_BACKPEDAL, ANIM_BACK, K_DOWNARROW, -1, -1, -1}, {"+moveleft", "step left", ID_MOVELEFT, ANIM_STEPLEFT, ',', -1, -1, -1}, {"+moveright", "step right", ID_MOVERIGHT, ANIM_STEPRIGHT, '.', -1, -1, -1}, {"+moveup", "up / jump", ID_MOVEUP, ANIM_JUMP, K_SPACE, -1, -1, -1}, {"+movedown", "down / crouch", ID_MOVEDOWN, ANIM_CROUCH, 'c', -1, -1, -1}, {"+left", "turn left", ID_LEFT, ANIM_TURNLEFT, K_LEFTARROW, -1, -1, -1}, {"+right", "turn right", ID_RIGHT, ANIM_TURNRIGHT, K_RIGHTARROW, -1, -1, -1}, {"+strafe", "sidestep / turn", ID_STRAFE, ANIM_IDLE, K_ALT, -1, -1, -1}, {"+lookup", "look up", ID_LOOKUP, ANIM_LOOKUP, K_PGDN, -1, -1, -1}, {"+lookdown", "look down", ID_LOOKDOWN, ANIM_LOOKDOWN, K_DEL, -1, -1, -1}, {"+mlook", "mouse look", ID_MOUSELOOK, ANIM_IDLE, '/', -1, -1, -1}, {"centerview", "center view", ID_CENTERVIEW, ANIM_IDLE, K_END, -1, -1, -1}, {"+zoom", "zoom view", ID_ZOOMVIEW, ANIM_IDLE, -1, -1, -1, -1}, {"weapon 1", "gauntlet", ID_WEAPON1, ANIM_WEAPON1, '1', -1, -1, -1}, {"weapon 2", "machinegun", ID_WEAPON2, ANIM_WEAPON2, '2', -1, -1, -1}, {"weapon 3", "shotgun", ID_WEAPON3, ANIM_WEAPON3, '3', -1, -1, -1}, {"weapon 4", "grenade launcher", ID_WEAPON4, ANIM_WEAPON4, '4', -1, -1, -1}, {"weapon 5", "rocket launcher", ID_WEAPON5, ANIM_WEAPON5, '5', -1, -1, -1}, {"weapon 6", "lightning", ID_WEAPON6, ANIM_WEAPON6, '6', -1, -1, -1}, {"weapon 7", "railgun", ID_WEAPON7, ANIM_WEAPON7, '7', -1, -1, -1}, {"weapon 8", "plasma gun", ID_WEAPON8, ANIM_WEAPON8, '8', -1, -1, -1}, {"weapon 9", "BFG", ID_WEAPON9, ANIM_WEAPON9, '9', -1, -1, -1}, {"weapon 10", "Grapple", ID_WEAPON10, ANIM_WEAPON10, -1, -1, -1, -1}, {"weapon 11", "nailgun", ID_WEAPON11, ANIM_WEAPON11, -1, -1, -1, -1}, {"weapon 12", "mine Launcher", ID_WEAPON12, ANIM_WEAPON12, -1, -1, -1, -1}, {"weapon 13", "chaingun", ID_WEAPON13, ANIM_WEAPON13, -1, -1, -1, -1}, {"+attack", "attack", ID_ATTACK, ANIM_ATTACK, K_CTRL, -1, -1, -1}, {"weapprev", "prev weapon", ID_WEAPPREV, ANIM_IDLE, '[', -1, -1, -1}, {"weapnext", "next weapon", ID_WEAPNEXT, ANIM_IDLE, ']', -1, -1, -1}, {"+button3", "gesture", ID_GESTURE, ANIM_GESTURE, K_MOUSE3, -1, -1, -1}, {"messagemode", "chat", ID_CHAT, ANIM_CHAT, 't', -1, -1, -1}, {"messagemode2", "chat - team", ID_CHAT2, ANIM_CHAT, -1, -1, -1, -1}, {"messagemode3", "chat - target", ID_CHAT3, ANIM_CHAT, -1, -1, -1, -1}, {"messagemode4", "chat - attacker", ID_CHAT4, ANIM_CHAT, -1, -1, -1, -1}, {"+voiprecord", "voice chat", ID_VOIP_TALK, ANIM_CHAT, 'q', -1, -1, -1}, {(char*)NULL, (char*)NULL, 0, 0, -1, -1, -1, -1}, }; static configcvar_t g_configcvars[] = { {"cl_run", 0, 0}, {"m_pitch", 0, 0}, {"cg_autoswitch", 0, 0}, {"sensitivity", 0, 0}, {"in_joystick", 0, 0}, {"joy_threshold", 0, 0}, {"m_filter", 0, 0}, {"cl_freelook", 0, 0}, {"cg_voipTeamOnly", 0, 0}, {NULL, 0, 0} }; static menucommon_s *g_movement_controls[] = { (menucommon_s *)&s_controls.alwaysrun, (menucommon_s *)&s_controls.run, (menucommon_s *)&s_controls.walkforward, (menucommon_s *)&s_controls.backpedal, (menucommon_s *)&s_controls.stepleft, (menucommon_s *)&s_controls.stepright, (menucommon_s *)&s_controls.moveup, (menucommon_s *)&s_controls.movedown, (menucommon_s *)&s_controls.turnleft, (menucommon_s *)&s_controls.turnright, (menucommon_s *)&s_controls.sidestep, NULL }; static menucommon_s *g_weapons_controls[] = { (menucommon_s *)&s_controls.attack, (menucommon_s *)&s_controls.nextweapon, (menucommon_s *)&s_controls.prevweapon, (menucommon_s *)&s_controls.autoswitch, (menucommon_s *)&s_controls.chainsaw, (menucommon_s *)&s_controls.machinegun, (menucommon_s *)&s_controls.shotgun, (menucommon_s *)&s_controls.grenadelauncher, (menucommon_s *)&s_controls.rocketlauncher, (menucommon_s *)&s_controls.lightning, (menucommon_s *)&s_controls.railgun, (menucommon_s *)&s_controls.plasma, (menucommon_s *)&s_controls.bfg, (menucommon_s *)&s_controls.grapple, (menucommon_s *)&s_controls.nailgun, (menucommon_s *)&s_controls.proxmine, (menucommon_s *)&s_controls.chaingun, NULL, }; static menucommon_s *g_looking_controls[] = { (menucommon_s *)&s_controls.sensitivity, (menucommon_s *)&s_controls.smoothmouse, (menucommon_s *)&s_controls.invertmouse, (menucommon_s *)&s_controls.lookup, (menucommon_s *)&s_controls.lookdown, (menucommon_s *)&s_controls.mouselook, (menucommon_s *)&s_controls.freelook, (menucommon_s *)&s_controls.centerview, (menucommon_s *)&s_controls.zoomview, (menucommon_s *)&s_controls.joyenable, (menucommon_s *)&s_controls.joythreshold, NULL, }; static menucommon_s *g_misc_controls[] = { (menucommon_s *)&s_controls.showscores, (menucommon_s *)&s_controls.useitem, (menucommon_s *)&s_controls.gesture, (menucommon_s *)&s_controls.chat, (menucommon_s *)&s_controls.chat2, (menucommon_s *)&s_controls.chat3, (menucommon_s *)&s_controls.chat4, (menucommon_s *)&s_controls.voip_talk, (menucommon_s *)&s_controls.voip_teamonly, NULL, }; static menucommon_s **g_controls[] = { g_movement_controls, g_looking_controls, g_weapons_controls, g_misc_controls, }; /* ================= Controls_InitCvars ================= */ static void Controls_InitCvars( void ) { int i; configcvar_t* cvarptr; cvarptr = g_configcvars; for (i=0; ;i++,cvarptr++) { if (!cvarptr->name) break; // get current value cvarptr->value = trap_Cvar_VariableValue( cvarptr->name ); // get default value trap_Cvar_Reset( cvarptr->name ); cvarptr->defaultvalue = trap_Cvar_VariableValue( cvarptr->name ); // restore current value trap_Cvar_SetValue( cvarptr->name, cvarptr->value ); } } /* ================= Controls_GetCvarDefault ================= */ static float Controls_GetCvarDefault( char* name ) { configcvar_t* cvarptr; int i; cvarptr = g_configcvars; for (i=0; ;i++,cvarptr++) { if (!cvarptr->name) return (0); if (!strcmp(cvarptr->name,name)) break; } return (cvarptr->defaultvalue); } /* ================= Controls_GetCvarValue ================= */ static float Controls_GetCvarValue( char* name ) { configcvar_t* cvarptr; int i; cvarptr = g_configcvars; for (i=0; ;i++,cvarptr++) { if (!cvarptr->name) return (0); if (!strcmp(cvarptr->name,name)) break; } return (cvarptr->value); } /* ================= Controls_UpdateModel ================= */ static void Controls_UpdateModel( int anim ) { VectorClear( s_controls.playerViewangles ); VectorClear( s_controls.playerMoveangles ); s_controls.playerViewangles[YAW] = 180 - 30; s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW]; s_controls.playerLegs = LEGS_IDLE; s_controls.playerTorso = TORSO_STAND; s_controls.playerWeapon = -1; s_controls.playerChat = qfalse; switch( anim ) { case ANIM_RUN: s_controls.playerLegs = LEGS_RUN; break; case ANIM_WALK: s_controls.playerLegs = LEGS_WALK; break; case ANIM_BACK: s_controls.playerLegs = LEGS_BACK; break; case ANIM_JUMP: s_controls.playerLegs = LEGS_JUMP; break; case ANIM_CROUCH: s_controls.playerLegs = LEGS_IDLECR; break; case ANIM_TURNLEFT: s_controls.playerViewangles[YAW] += 90; break; case ANIM_TURNRIGHT: s_controls.playerViewangles[YAW] -= 90; break; case ANIM_STEPLEFT: s_controls.playerLegs = LEGS_WALK; s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW] + 90; break; case ANIM_STEPRIGHT: s_controls.playerLegs = LEGS_WALK; s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW] - 90; break; case ANIM_LOOKUP: s_controls.playerViewangles[PITCH] = -45; break; case ANIM_LOOKDOWN: s_controls.playerViewangles[PITCH] = 45; break; case ANIM_WEAPON1: s_controls.playerWeapon = WP_GAUNTLET; break; case ANIM_WEAPON2: s_controls.playerWeapon = WP_MACHINEGUN; break; case ANIM_WEAPON3: s_controls.playerWeapon = WP_SHOTGUN; break; case ANIM_WEAPON4: s_controls.playerWeapon = WP_GRENADE_LAUNCHER; break; case ANIM_WEAPON5: s_controls.playerWeapon = WP_ROCKET_LAUNCHER; break; case ANIM_WEAPON6: s_controls.playerWeapon = WP_LIGHTNING; break; case ANIM_WEAPON7: s_controls.playerWeapon = WP_RAILGUN; break; case ANIM_WEAPON8: s_controls.playerWeapon = WP_PLASMAGUN; break; case ANIM_WEAPON9: s_controls.playerWeapon = WP_BFG; break; case ANIM_WEAPON10: s_controls.playerWeapon = WP_GRAPPLING_HOOK; break; case ANIM_WEAPON11: s_controls.playerWeapon = WP_NAILGUN; break; case ANIM_WEAPON12: s_controls.playerWeapon = WP_PROX_LAUNCHER; break; case ANIM_WEAPON13: s_controls.playerWeapon = WP_CHAINGUN; break; case ANIM_ATTACK: s_controls.playerTorso = TORSO_ATTACK; break; case ANIM_GESTURE: s_controls.playerTorso = TORSO_GESTURE; break; case ANIM_DIE: s_controls.playerLegs = BOTH_DEATH1; s_controls.playerTorso = BOTH_DEATH1; s_controls.playerWeapon = WP_NONE; break; case ANIM_CHAT: s_controls.playerChat = qtrue; break; default: break; } UI_PlayerInfo_SetInfo( &s_controls.playerinfo, s_controls.playerLegs, s_controls.playerTorso, s_controls.playerViewangles, s_controls.playerMoveangles, s_controls.playerWeapon, s_controls.playerChat ); } /* ================= Controls_Update ================= */ static void Controls_Update( void ) { int i; int j; int y; menucommon_s **controls; menucommon_s *control; // disable all controls in all groups for( i = 0; i < C_MAX; i++ ) { controls = g_controls[i]; // bk001204 - parentheses for( j = 0; (control = controls[j]) ; j++ ) { control->flags |= (QMF_HIDDEN|QMF_INACTIVE); } } controls = g_controls[s_controls.section]; // enable controls in active group (and count number of items for vertical centering) // bk001204 - parentheses for( j = 0; (control = controls[j]) ; j++ ) { control->flags &= ~(QMF_GRAYED|QMF_HIDDEN|QMF_INACTIVE); } // position controls y = ( SCREEN_HEIGHT - j * SMALLCHAR_HEIGHT ) / 2; // bk001204 - parentheses for( j = 0; (control = controls[j]) ; j++, y += SMALLCHAR_HEIGHT ) { control->x = 320; control->y = y; control->left = 320 - 19*SMALLCHAR_WIDTH; control->right = 320 + 21*SMALLCHAR_WIDTH; control->top = y; control->bottom = y + SMALLCHAR_HEIGHT; } if( s_controls.waitingforkey ) { // disable everybody for( i = 0; i < s_controls.menu.nitems; i++ ) { ((menucommon_s*)(s_controls.menu.items[i]))->flags |= QMF_GRAYED; } // enable action item ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->flags &= ~QMF_GRAYED; // don't gray out player's name s_controls.name.generic.flags &= ~QMF_GRAYED; return; } // enable everybody for( i = 0; i < s_controls.menu.nitems; i++ ) { ((menucommon_s*)(s_controls.menu.items[i]))->flags &= ~QMF_GRAYED; } // makes sure flags are right on the group selection controls s_controls.looking.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); s_controls.movement.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); s_controls.weapons.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); s_controls.misc.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); s_controls.looking.generic.flags |= QMF_PULSEIFFOCUS; s_controls.movement.generic.flags |= QMF_PULSEIFFOCUS; s_controls.weapons.generic.flags |= QMF_PULSEIFFOCUS; s_controls.misc.generic.flags |= QMF_PULSEIFFOCUS; // set buttons switch( s_controls.section ) { case C_MOVEMENT: s_controls.movement.generic.flags &= ~QMF_PULSEIFFOCUS; s_controls.movement.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); break; case C_LOOKING: s_controls.looking.generic.flags &= ~QMF_PULSEIFFOCUS; s_controls.looking.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); break; case C_WEAPONS: s_controls.weapons.generic.flags &= ~QMF_PULSEIFFOCUS; s_controls.weapons.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); break; case C_MISC: s_controls.misc.generic.flags &= ~QMF_PULSEIFFOCUS; s_controls.misc.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS); break; } } /* ================= Controls_DrawKeyBinding ================= */ static void Controls_DrawKeyBinding( void *self ) { menuaction_s* a; int x; int y; int b1; int b2; qboolean c; char name[32]; char name2[32]; a = (menuaction_s*) self; x = a->generic.x; y = a->generic.y; c = (Menu_ItemAtCursor( a->generic.parent ) == a); b1 = g_bindings[a->generic.id].bind1; if (b1 == -1) strcpy(name,"???"); else { trap_Key_KeynumToStringBuf( b1, name, 32 ); Q_strupr(name); b2 = g_bindings[a->generic.id].bind2; if (b2 != -1) { trap_Key_KeynumToStringBuf( b2, name2, 32 ); Q_strupr(name2); strcat( name, " or " ); strcat( name, name2 ); } } if (c) { UI_FillRect( a->generic.left, a->generic.top, a->generic.right-a->generic.left+1, a->generic.bottom-a->generic.top+1, listbar_color ); UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, text_color_highlight ); UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT|UI_PULSE, text_color_highlight ); if (s_controls.waitingforkey) { UI_DrawChar( x, y, '=', UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight); UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Waiting for new key ... ESCAPE to cancel", UI_SMALLFONT|UI_CENTER|UI_PULSE, colorWhite ); } else { UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight); UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.78, "Press ENTER or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite ); UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.82, "Press BACKSPACE to clear", UI_SMALLFONT|UI_CENTER, colorWhite ); } } else { if (a->generic.flags & QMF_GRAYED) { UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, text_color_disabled ); UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT, text_color_disabled ); } else { UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, controls_binding_color ); UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT, controls_binding_color ); } } } /* ================= Controls_StatusBar ================= */ static void Controls_StatusBar( void *self ) { UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Use Arrow Keys or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite ); } /* ================= Controls_DrawPlayer ================= */ static void Controls_DrawPlayer( void *self ) { menubitmap_s *b; char buf[MAX_QPATH]; trap_Cvar_VariableStringBuffer( "model", buf, sizeof( buf ) ); if ( strcmp( buf, s_controls.playerModel ) != 0 ) { UI_PlayerInfo_SetModel( &s_controls.playerinfo, buf ); strcpy( s_controls.playerModel, buf ); Controls_UpdateModel( ANIM_IDLE ); } b = (menubitmap_s*) self; UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_controls.playerinfo, uis.realtime/2 ); } /* ================= Controls_GetKeyAssignment ================= */ static void Controls_GetKeyAssignment (char *command, int *twokeys) { int count; int j; char b[256]; twokeys[0] = twokeys[1] = -1; count = 0; for ( j = 0; j < 256; j++ ) { trap_Key_GetBindingBuf( j, b, 256 ); if ( *b == 0 ) { continue; } if ( !Q_stricmp( b, command ) ) { twokeys[count] = j; count++; if (count == 2) break; } } } /* ================= Controls_GetConfig ================= */ static void Controls_GetConfig( void ) { int i; int twokeys[2]; bind_t* bindptr; // put the bindings into a local store bindptr = g_bindings; // iterate each command, get its numeric binding for (i=0; ;i++,bindptr++) { if (!bindptr->label) break; Controls_GetKeyAssignment(bindptr->command, twokeys); bindptr->bind1 = twokeys[0]; bindptr->bind2 = twokeys[1]; } s_controls.invertmouse.curvalue = Controls_GetCvarValue( "m_pitch" ) < 0; s_controls.smoothmouse.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "m_filter" ) ); s_controls.alwaysrun.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_run" ) ); s_controls.autoswitch.curvalue = UI_ClampCvar( 0, 4, Controls_GetCvarValue( "cg_autoswitch" ) ); s_controls.sensitivity.curvalue = UI_ClampCvar( 2, 30, Controls_GetCvarValue( "sensitivity" ) ); s_controls.joyenable.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "in_joystick" ) ); s_controls.joythreshold.curvalue = UI_ClampCvar( 0.05f, 0.75f, Controls_GetCvarValue( "joy_threshold" ) ); s_controls.freelook.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) ); s_controls.voip_teamonly.curvalue= UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cg_voipTeamOnly" ) ); } /* ================= Controls_SetConfig ================= */ static void Controls_SetConfig( void ) { int i; bind_t* bindptr; // set the bindings from the local store bindptr = g_bindings; // iterate each command, get its numeric binding for (i=0; ;i++,bindptr++) { if (!bindptr->label) break; if (bindptr->bind1 != -1) { trap_Key_SetBinding( bindptr->bind1, bindptr->command ); if (bindptr->bind2 != -1) trap_Key_SetBinding( bindptr->bind2, bindptr->command ); } } if ( s_controls.invertmouse.curvalue ) trap_Cvar_SetValue( "m_pitch", -fabs( trap_Cvar_VariableValue( "m_pitch" ) ) ); else trap_Cvar_SetValue( "m_pitch", fabs( trap_Cvar_VariableValue( "m_pitch" ) ) ); trap_Cvar_SetValue( "m_filter", s_controls.smoothmouse.curvalue ); trap_Cvar_SetValue( "cl_run", s_controls.alwaysrun.curvalue ); trap_Cvar_SetValue( "cg_autoswitch", s_controls.autoswitch.curvalue ); trap_Cvar_SetValue( "sensitivity", s_controls.sensitivity.curvalue ); trap_Cvar_SetValue( "in_joystick", s_controls.joyenable.curvalue ); trap_Cvar_SetValue( "joy_threshold", s_controls.joythreshold.curvalue ); trap_Cvar_SetValue( "cl_freelook", s_controls.freelook.curvalue ); trap_Cvar_SetValue( "cg_voipTeamOnly", s_controls.voip_teamonly.curvalue); trap_Cmd_ExecuteText( EXEC_APPEND, "in_restart\n" ); } /* ================= Controls_SetDefaults ================= */ static void Controls_SetDefaults( void ) { int i; bind_t* bindptr; // set the bindings from the local store bindptr = g_bindings; // iterate each command, set its default binding for (i=0; ;i++,bindptr++) { if (!bindptr->label) break; bindptr->bind1 = bindptr->defaultbind1; bindptr->bind2 = bindptr->defaultbind2; } s_controls.invertmouse.curvalue = Controls_GetCvarDefault( "m_pitch" ) < 0; s_controls.smoothmouse.curvalue = Controls_GetCvarDefault( "m_filter" ); s_controls.alwaysrun.curvalue = Controls_GetCvarDefault( "cl_run" ); s_controls.autoswitch.curvalue = Controls_GetCvarDefault( "cg_autoswitch" ); s_controls.sensitivity.curvalue = Controls_GetCvarDefault( "sensitivity" ); s_controls.joyenable.curvalue = Controls_GetCvarDefault( "in_joystick" ); s_controls.joythreshold.curvalue = Controls_GetCvarDefault( "joy_threshold" ); s_controls.freelook.curvalue = Controls_GetCvarDefault( "cl_freelook" ); s_controls.voip_teamonly.curvalue= Controls_GetCvarDefault( "cg_voipTeamOnly"); } /* ================= Controls_MenuKey ================= */ static sfxHandle_t Controls_MenuKey( int key ) { int id; int i; qboolean found; bind_t* bindptr; found = qfalse; if (!s_controls.waitingforkey) { switch (key) { case K_BACKSPACE: case K_DEL: case K_KP_DEL: key = -1; break; case K_MOUSE2: case K_ESCAPE: if (s_controls.changesmade) Controls_SetConfig(); goto ignorekey; default: goto ignorekey; } } else { if (key & K_CHAR_FLAG) goto ignorekey; switch (key) { case K_ESCAPE: s_controls.waitingforkey = qfalse; Controls_Update(); return (menu_out_sound); case '`': goto ignorekey; } } s_controls.changesmade = qtrue; if (key != -1) { // remove from any other bind bindptr = g_bindings; for (i=0; ;i++,bindptr++) { if (!bindptr->label) break; if (bindptr->bind2 == key) bindptr->bind2 = -1; if (bindptr->bind1 == key) { bindptr->bind1 = bindptr->bind2; bindptr->bind2 = -1; } } } // assign key to local store id = ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->id; bindptr = g_bindings; for (i=0; ;i++,bindptr++) { if (!bindptr->label) break; if (bindptr->id == id) { found = qtrue; if (key == -1) { if( bindptr->bind1 != -1 ) { trap_Key_SetBinding( bindptr->bind1, "" ); bindptr->bind1 = -1; } if( bindptr->bind2 != -1 ) { trap_Key_SetBinding( bindptr->bind2, "" ); bindptr->bind2 = -1; } } else if (bindptr->bind1 == -1) { bindptr->bind1 = key; } else if (bindptr->bind1 != key && bindptr->bind2 == -1) { bindptr->bind2 = key; } else { trap_Key_SetBinding( bindptr->bind1, "" ); trap_Key_SetBinding( bindptr->bind2, "" ); bindptr->bind1 = key; bindptr->bind2 = -1; } break; } } s_controls.waitingforkey = qfalse; if (found) { Controls_Update(); return (menu_out_sound); } ignorekey: return Menu_DefaultKey( &s_controls.menu, key ); } /* ================= Controls_ResetDefaults_Action ================= */ static void Controls_ResetDefaults_Action( qboolean result ) { if( !result ) { return; } s_controls.changesmade = qtrue; Controls_SetDefaults(); Controls_Update(); } /* ================= Controls_ResetDefaults_Draw ================= */ static void Controls_ResetDefaults_Draw( void ) { UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This will reset all", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "controls to their default values.", UI_CENTER|UI_SMALLFONT, color_yellow ); } /* ================= Controls_MenuEvent ================= */ static void Controls_MenuEvent( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_MOVEMENT: if (event == QM_ACTIVATED) { s_controls.section = C_MOVEMENT; Controls_Update(); } break; case ID_LOOKING: if (event == QM_ACTIVATED) { s_controls.section = C_LOOKING; Controls_Update(); } break; case ID_WEAPONS: if (event == QM_ACTIVATED) { s_controls.section = C_WEAPONS; Controls_Update(); } break; case ID_MISC: if (event == QM_ACTIVATED) { s_controls.section = C_MISC; Controls_Update(); } break; case ID_DEFAULTS: if (event == QM_ACTIVATED) { UI_ConfirmMenu( "SET TO DEFAULTS?", Controls_ResetDefaults_Draw, Controls_ResetDefaults_Action ); } break; case ID_BACK: if (event == QM_ACTIVATED) { if (s_controls.changesmade) Controls_SetConfig(); UI_PopMenu(); } break; case ID_SAVEANDEXIT: if (event == QM_ACTIVATED) { Controls_SetConfig(); UI_PopMenu(); } break; case ID_EXIT: if (event == QM_ACTIVATED) { UI_PopMenu(); } break; case ID_FREELOOK: case ID_MOUSESPEED: case ID_INVERTMOUSE: case ID_SMOOTHMOUSE: case ID_ALWAYSRUN: case ID_AUTOSWITCH: case ID_VOIP_TEAMONLY: case ID_JOYENABLE: case ID_JOYTHRESHOLD: if (event == QM_ACTIVATED) { s_controls.changesmade = qtrue; } break; } } /* ================= Controls_ActionEvent ================= */ static void Controls_ActionEvent( void* ptr, int event ) { if (event == QM_LOSTFOCUS) { Controls_UpdateModel( ANIM_IDLE ); } else if (event == QM_GOTFOCUS) { Controls_UpdateModel( g_bindings[((menucommon_s*)ptr)->id].anim ); } else if ((event == QM_ACTIVATED) && !s_controls.waitingforkey) { s_controls.waitingforkey = 1; Controls_Update(); } } /* ================= Controls_InitModel ================= */ static void Controls_InitModel( void ) { memset( &s_controls.playerinfo, 0, sizeof(playerInfo_t) ); UI_PlayerInfo_SetModel( &s_controls.playerinfo, UI_Cvar_VariableString( "model" ) ); Controls_UpdateModel( ANIM_IDLE ); } /* ================= Controls_InitWeapons ================= */ static void Controls_InitWeapons( void ) { gitem_t * item; for ( item = bg_itemlist + 1 ; item->classname ; item++ ) { if ( item->giType != IT_WEAPON ) { continue; } trap_R_RegisterModel( item->world_model[0] ); } } /* ================= Controls_MenuInit ================= */ static void Controls_MenuInit( void ) { static char playername[32]; // zero set all our globals memset( &s_controls, 0 ,sizeof(controls_t) ); Controls_Cache(); s_controls.menu.key = Controls_MenuKey; s_controls.menu.wrapAround = qtrue; s_controls.menu.fullscreen = qtrue; s_controls.banner.generic.type = MTYPE_BTEXT; s_controls.banner.generic.flags = QMF_CENTER_JUSTIFY; s_controls.banner.generic.x = 320; s_controls.banner.generic.y = 16; s_controls.banner.string = "CONTROLS"; s_controls.banner.color = color_white; s_controls.banner.style = UI_CENTER; s_controls.framel.generic.type = MTYPE_BITMAP; s_controls.framel.generic.name = ART_FRAMEL; s_controls.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_controls.framel.generic.x = 0; s_controls.framel.generic.y = 78; s_controls.framel.width = 256; s_controls.framel.height = 329; s_controls.framer.generic.type = MTYPE_BITMAP; s_controls.framer.generic.name = ART_FRAMER; s_controls.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_controls.framer.generic.x = 376; s_controls.framer.generic.y = 76; s_controls.framer.width = 256; s_controls.framer.height = 334; s_controls.looking.generic.type = MTYPE_PTEXT; s_controls.looking.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_controls.looking.generic.id = ID_LOOKING; s_controls.looking.generic.callback = Controls_MenuEvent; s_controls.looking.generic.x = 152; s_controls.looking.generic.y = 240 - 2 * PROP_HEIGHT; s_controls.looking.string = "LOOK"; s_controls.looking.style = UI_RIGHT; s_controls.looking.color = color_red; s_controls.movement.generic.type = MTYPE_PTEXT; s_controls.movement.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_controls.movement.generic.id = ID_MOVEMENT; s_controls.movement.generic.callback = Controls_MenuEvent; s_controls.movement.generic.x = 152; s_controls.movement.generic.y = 240 - PROP_HEIGHT; s_controls.movement.string = "MOVE"; s_controls.movement.style = UI_RIGHT; s_controls.movement.color = color_red; s_controls.weapons.generic.type = MTYPE_PTEXT; s_controls.weapons.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_controls.weapons.generic.id = ID_WEAPONS; s_controls.weapons.generic.callback = Controls_MenuEvent; s_controls.weapons.generic.x = 152; s_controls.weapons.generic.y = 240; s_controls.weapons.string = "SHOOT"; s_controls.weapons.style = UI_RIGHT; s_controls.weapons.color = color_red; s_controls.misc.generic.type = MTYPE_PTEXT; s_controls.misc.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_controls.misc.generic.id = ID_MISC; s_controls.misc.generic.callback = Controls_MenuEvent; s_controls.misc.generic.x = 152; s_controls.misc.generic.y = 240 + PROP_HEIGHT; s_controls.misc.string = "MISC"; s_controls.misc.style = UI_RIGHT; s_controls.misc.color = color_red; s_controls.back.generic.type = MTYPE_BITMAP; s_controls.back.generic.name = ART_BACK0; s_controls.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_controls.back.generic.x = 0; s_controls.back.generic.y = 480-64; s_controls.back.generic.id = ID_BACK; s_controls.back.generic.callback = Controls_MenuEvent; s_controls.back.width = 128; s_controls.back.height = 64; s_controls.back.focuspic = ART_BACK1; s_controls.player.generic.type = MTYPE_BITMAP; s_controls.player.generic.flags = QMF_INACTIVE; s_controls.player.generic.ownerdraw = Controls_DrawPlayer; s_controls.player.generic.x = 400; s_controls.player.generic.y = -40; s_controls.player.width = 32*10; s_controls.player.height = 56*10; s_controls.walkforward.generic.type = MTYPE_ACTION; s_controls.walkforward.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.walkforward.generic.callback = Controls_ActionEvent; s_controls.walkforward.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.walkforward.generic.id = ID_FORWARD; s_controls.backpedal.generic.type = MTYPE_ACTION; s_controls.backpedal.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.backpedal.generic.callback = Controls_ActionEvent; s_controls.backpedal.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.backpedal.generic.id = ID_BACKPEDAL; s_controls.stepleft.generic.type = MTYPE_ACTION; s_controls.stepleft.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.stepleft.generic.callback = Controls_ActionEvent; s_controls.stepleft.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.stepleft.generic.id = ID_MOVELEFT; s_controls.stepright.generic.type = MTYPE_ACTION; s_controls.stepright.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.stepright.generic.callback = Controls_ActionEvent; s_controls.stepright.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.stepright.generic.id = ID_MOVERIGHT; s_controls.moveup.generic.type = MTYPE_ACTION; s_controls.moveup.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.moveup.generic.callback = Controls_ActionEvent; s_controls.moveup.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.moveup.generic.id = ID_MOVEUP; s_controls.movedown.generic.type = MTYPE_ACTION; s_controls.movedown.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.movedown.generic.callback = Controls_ActionEvent; s_controls.movedown.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.movedown.generic.id = ID_MOVEDOWN; s_controls.turnleft.generic.type = MTYPE_ACTION; s_controls.turnleft.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.turnleft.generic.callback = Controls_ActionEvent; s_controls.turnleft.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.turnleft.generic.id = ID_LEFT; s_controls.turnright.generic.type = MTYPE_ACTION; s_controls.turnright.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.turnright.generic.callback = Controls_ActionEvent; s_controls.turnright.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.turnright.generic.id = ID_RIGHT; s_controls.sidestep.generic.type = MTYPE_ACTION; s_controls.sidestep.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.sidestep.generic.callback = Controls_ActionEvent; s_controls.sidestep.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.sidestep.generic.id = ID_STRAFE; s_controls.run.generic.type = MTYPE_ACTION; s_controls.run.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.run.generic.callback = Controls_ActionEvent; s_controls.run.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.run.generic.id = ID_SPEED; s_controls.chainsaw.generic.type = MTYPE_ACTION; s_controls.chainsaw.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chainsaw.generic.callback = Controls_ActionEvent; s_controls.chainsaw.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chainsaw.generic.id = ID_WEAPON1; s_controls.machinegun.generic.type = MTYPE_ACTION; s_controls.machinegun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.machinegun.generic.callback = Controls_ActionEvent; s_controls.machinegun.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.machinegun.generic.id = ID_WEAPON2; s_controls.shotgun.generic.type = MTYPE_ACTION; s_controls.shotgun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.shotgun.generic.callback = Controls_ActionEvent; s_controls.shotgun.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.shotgun.generic.id = ID_WEAPON3; s_controls.grenadelauncher.generic.type = MTYPE_ACTION; s_controls.grenadelauncher.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.grenadelauncher.generic.callback = Controls_ActionEvent; s_controls.grenadelauncher.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.grenadelauncher.generic.id = ID_WEAPON4; s_controls.rocketlauncher.generic.type = MTYPE_ACTION; s_controls.rocketlauncher.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.rocketlauncher.generic.callback = Controls_ActionEvent; s_controls.rocketlauncher.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.rocketlauncher.generic.id = ID_WEAPON5; s_controls.lightning.generic.type = MTYPE_ACTION; s_controls.lightning.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.lightning.generic.callback = Controls_ActionEvent; s_controls.lightning.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.lightning.generic.id = ID_WEAPON6; s_controls.railgun.generic.type = MTYPE_ACTION; s_controls.railgun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.railgun.generic.callback = Controls_ActionEvent; s_controls.railgun.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.railgun.generic.id = ID_WEAPON7; s_controls.plasma.generic.type = MTYPE_ACTION; s_controls.plasma.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.plasma.generic.callback = Controls_ActionEvent; s_controls.plasma.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.plasma.generic.id = ID_WEAPON8; s_controls.bfg.generic.type = MTYPE_ACTION; s_controls.bfg.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.bfg.generic.callback = Controls_ActionEvent; s_controls.bfg.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.bfg.generic.id = ID_WEAPON9; s_controls.grapple.generic.type = MTYPE_ACTION; s_controls.grapple.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.grapple.generic.callback = Controls_ActionEvent; s_controls.grapple.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.grapple.generic.id = ID_WEAPON10; s_controls.nailgun.generic.type = MTYPE_ACTION; s_controls.nailgun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.nailgun.generic.callback = Controls_ActionEvent; s_controls.nailgun.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.nailgun.generic.id = ID_WEAPON11; s_controls.proxmine.generic.type = MTYPE_ACTION; s_controls.proxmine.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.proxmine.generic.callback = Controls_ActionEvent; s_controls.proxmine.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.proxmine.generic.id = ID_WEAPON12; s_controls.chaingun.generic.type = MTYPE_ACTION; s_controls.chaingun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chaingun.generic.callback = Controls_ActionEvent; s_controls.chaingun.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chaingun.generic.id = ID_WEAPON13; s_controls.attack.generic.type = MTYPE_ACTION; s_controls.attack.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.attack.generic.callback = Controls_ActionEvent; s_controls.attack.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.attack.generic.id = ID_ATTACK; s_controls.prevweapon.generic.type = MTYPE_ACTION; s_controls.prevweapon.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.prevweapon.generic.callback = Controls_ActionEvent; s_controls.prevweapon.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.prevweapon.generic.id = ID_WEAPPREV; s_controls.nextweapon.generic.type = MTYPE_ACTION; s_controls.nextweapon.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.nextweapon.generic.callback = Controls_ActionEvent; s_controls.nextweapon.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.nextweapon.generic.id = ID_WEAPNEXT; s_controls.lookup.generic.type = MTYPE_ACTION; s_controls.lookup.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.lookup.generic.callback = Controls_ActionEvent; s_controls.lookup.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.lookup.generic.id = ID_LOOKUP; s_controls.lookdown.generic.type = MTYPE_ACTION; s_controls.lookdown.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.lookdown.generic.callback = Controls_ActionEvent; s_controls.lookdown.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.lookdown.generic.id = ID_LOOKDOWN; s_controls.mouselook.generic.type = MTYPE_ACTION; s_controls.mouselook.generic.flags = QMF_LEFT_JUSTIFY|QMF_HIGHLIGHT_IF_FOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.mouselook.generic.callback = Controls_ActionEvent; s_controls.mouselook.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.mouselook.generic.id = ID_MOUSELOOK; s_controls.freelook.generic.type = MTYPE_RADIOBUTTON; s_controls.freelook.generic.flags = QMF_SMALLFONT; s_controls.freelook.generic.x = SCREEN_WIDTH/2; s_controls.freelook.generic.name = "free look"; s_controls.freelook.generic.id = ID_FREELOOK; s_controls.freelook.generic.callback = Controls_MenuEvent; s_controls.freelook.generic.statusbar = Controls_StatusBar; s_controls.centerview.generic.type = MTYPE_ACTION; s_controls.centerview.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.centerview.generic.callback = Controls_ActionEvent; s_controls.centerview.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.centerview.generic.id = ID_CENTERVIEW; s_controls.zoomview.generic.type = MTYPE_ACTION; s_controls.zoomview.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.zoomview.generic.callback = Controls_ActionEvent; s_controls.zoomview.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.zoomview.generic.id = ID_ZOOMVIEW; s_controls.useitem.generic.type = MTYPE_ACTION; s_controls.useitem.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.useitem.generic.callback = Controls_ActionEvent; s_controls.useitem.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.useitem.generic.id = ID_USEITEM; s_controls.showscores.generic.type = MTYPE_ACTION; s_controls.showscores.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.showscores.generic.callback = Controls_ActionEvent; s_controls.showscores.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.showscores.generic.id = ID_SHOWSCORES; s_controls.invertmouse.generic.type = MTYPE_RADIOBUTTON; s_controls.invertmouse.generic.flags = QMF_SMALLFONT; s_controls.invertmouse.generic.x = SCREEN_WIDTH/2; s_controls.invertmouse.generic.name = "invert mouse"; s_controls.invertmouse.generic.id = ID_INVERTMOUSE; s_controls.invertmouse.generic.callback = Controls_MenuEvent; s_controls.invertmouse.generic.statusbar = Controls_StatusBar; s_controls.smoothmouse.generic.type = MTYPE_RADIOBUTTON; s_controls.smoothmouse.generic.flags = QMF_SMALLFONT; s_controls.smoothmouse.generic.x = SCREEN_WIDTH/2; s_controls.smoothmouse.generic.name = "smooth mouse"; s_controls.smoothmouse.generic.id = ID_SMOOTHMOUSE; s_controls.smoothmouse.generic.callback = Controls_MenuEvent; s_controls.smoothmouse.generic.statusbar = Controls_StatusBar; s_controls.alwaysrun.generic.type = MTYPE_RADIOBUTTON; s_controls.alwaysrun.generic.flags = QMF_SMALLFONT; s_controls.alwaysrun.generic.x = SCREEN_WIDTH/2; s_controls.alwaysrun.generic.name = "always run"; s_controls.alwaysrun.generic.id = ID_ALWAYSRUN; s_controls.alwaysrun.generic.callback = Controls_MenuEvent; s_controls.alwaysrun.generic.statusbar = Controls_StatusBar; s_controls.autoswitch.generic.type = MTYPE_SPINCONTROL; s_controls.autoswitch.generic.flags = QMF_SMALLFONT; s_controls.autoswitch.generic.x = SCREEN_WIDTH/2; s_controls.autoswitch.generic.name = "autoswitch weapons"; s_controls.autoswitch.generic.id = ID_AUTOSWITCH; s_controls.autoswitch.generic.callback = Controls_MenuEvent; s_controls.autoswitch.generic.statusbar = Controls_StatusBar; s_controls.autoswitch.itemnames = autoswitch_items; s_controls.sensitivity.generic.type = MTYPE_SLIDER; s_controls.sensitivity.generic.x = SCREEN_WIDTH/2; s_controls.sensitivity.generic.flags = QMF_SMALLFONT; s_controls.sensitivity.generic.name = "mouse speed"; s_controls.sensitivity.generic.id = ID_MOUSESPEED; s_controls.sensitivity.generic.callback = Controls_MenuEvent; s_controls.sensitivity.minvalue = 2; s_controls.sensitivity.maxvalue = 30; s_controls.sensitivity.generic.statusbar = Controls_StatusBar; s_controls.gesture.generic.type = MTYPE_ACTION; s_controls.gesture.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.gesture.generic.callback = Controls_ActionEvent; s_controls.gesture.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.gesture.generic.id = ID_GESTURE; s_controls.chat.generic.type = MTYPE_ACTION; s_controls.chat.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chat.generic.callback = Controls_ActionEvent; s_controls.chat.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chat.generic.id = ID_CHAT; s_controls.chat2.generic.type = MTYPE_ACTION; s_controls.chat2.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chat2.generic.callback = Controls_ActionEvent; s_controls.chat2.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chat2.generic.id = ID_CHAT2; s_controls.chat3.generic.type = MTYPE_ACTION; s_controls.chat3.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chat3.generic.callback = Controls_ActionEvent; s_controls.chat3.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chat3.generic.id = ID_CHAT3; s_controls.chat4.generic.type = MTYPE_ACTION; s_controls.chat4.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.chat4.generic.callback = Controls_ActionEvent; s_controls.chat4.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.chat4.generic.id = ID_CHAT4; s_controls.voip_talk.generic.type = MTYPE_ACTION; s_controls.voip_talk.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN; s_controls.voip_talk.generic.callback = Controls_ActionEvent; s_controls.voip_talk.generic.ownerdraw = Controls_DrawKeyBinding; s_controls.voip_talk.generic.id = ID_VOIP_TALK; s_controls.voip_teamonly.generic.type = MTYPE_RADIOBUTTON; s_controls.voip_teamonly.generic.flags = QMF_SMALLFONT; s_controls.voip_teamonly.generic.x = SCREEN_WIDTH/2; s_controls.voip_teamonly.generic.name = "teamonly voicechat"; s_controls.voip_teamonly.generic.id = ID_VOIP_TEAMONLY; s_controls.voip_teamonly.generic.callback = Controls_MenuEvent; s_controls.voip_teamonly.generic.statusbar = Controls_StatusBar; s_controls.joyenable.generic.type = MTYPE_RADIOBUTTON; s_controls.joyenable.generic.flags = QMF_SMALLFONT; s_controls.joyenable.generic.x = SCREEN_WIDTH/2; s_controls.joyenable.generic.name = "joystick"; s_controls.joyenable.generic.id = ID_JOYENABLE; s_controls.joyenable.generic.callback = Controls_MenuEvent; s_controls.joyenable.generic.statusbar = Controls_StatusBar; s_controls.joythreshold.generic.type = MTYPE_SLIDER; s_controls.joythreshold.generic.x = SCREEN_WIDTH/2; s_controls.joythreshold.generic.flags = QMF_SMALLFONT; s_controls.joythreshold.generic.name = "joystick threshold"; s_controls.joythreshold.generic.id = ID_JOYTHRESHOLD; s_controls.joythreshold.generic.callback = Controls_MenuEvent; s_controls.joythreshold.minvalue = 0.05f; s_controls.joythreshold.maxvalue = 0.75f; s_controls.joythreshold.generic.statusbar = Controls_StatusBar; s_controls.name.generic.type = MTYPE_PTEXT; s_controls.name.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE; s_controls.name.generic.x = 320; s_controls.name.generic.y = 440; s_controls.name.string = playername; s_controls.name.style = UI_CENTER; s_controls.name.color = text_color_normal; Menu_AddItem( &s_controls.menu, &s_controls.banner ); Menu_AddItem( &s_controls.menu, &s_controls.framel ); Menu_AddItem( &s_controls.menu, &s_controls.framer ); Menu_AddItem( &s_controls.menu, &s_controls.player ); Menu_AddItem( &s_controls.menu, &s_controls.name ); Menu_AddItem( &s_controls.menu, &s_controls.looking ); Menu_AddItem( &s_controls.menu, &s_controls.movement ); Menu_AddItem( &s_controls.menu, &s_controls.weapons ); Menu_AddItem( &s_controls.menu, &s_controls.misc ); Menu_AddItem( &s_controls.menu, &s_controls.sensitivity ); Menu_AddItem( &s_controls.menu, &s_controls.smoothmouse ); Menu_AddItem( &s_controls.menu, &s_controls.invertmouse ); Menu_AddItem( &s_controls.menu, &s_controls.lookup ); Menu_AddItem( &s_controls.menu, &s_controls.lookdown ); Menu_AddItem( &s_controls.menu, &s_controls.mouselook ); Menu_AddItem( &s_controls.menu, &s_controls.freelook ); Menu_AddItem( &s_controls.menu, &s_controls.centerview ); Menu_AddItem( &s_controls.menu, &s_controls.zoomview ); Menu_AddItem( &s_controls.menu, &s_controls.joyenable ); Menu_AddItem( &s_controls.menu, &s_controls.joythreshold ); Menu_AddItem( &s_controls.menu, &s_controls.alwaysrun ); Menu_AddItem( &s_controls.menu, &s_controls.run ); Menu_AddItem( &s_controls.menu, &s_controls.walkforward ); Menu_AddItem( &s_controls.menu, &s_controls.backpedal ); Menu_AddItem( &s_controls.menu, &s_controls.stepleft ); Menu_AddItem( &s_controls.menu, &s_controls.stepright ); Menu_AddItem( &s_controls.menu, &s_controls.moveup ); Menu_AddItem( &s_controls.menu, &s_controls.movedown ); Menu_AddItem( &s_controls.menu, &s_controls.turnleft ); Menu_AddItem( &s_controls.menu, &s_controls.turnright ); Menu_AddItem( &s_controls.menu, &s_controls.sidestep ); Menu_AddItem( &s_controls.menu, &s_controls.attack ); Menu_AddItem( &s_controls.menu, &s_controls.nextweapon ); Menu_AddItem( &s_controls.menu, &s_controls.prevweapon ); Menu_AddItem( &s_controls.menu, &s_controls.autoswitch ); Menu_AddItem( &s_controls.menu, &s_controls.chainsaw ); Menu_AddItem( &s_controls.menu, &s_controls.machinegun ); Menu_AddItem( &s_controls.menu, &s_controls.shotgun ); Menu_AddItem( &s_controls.menu, &s_controls.grenadelauncher ); Menu_AddItem( &s_controls.menu, &s_controls.rocketlauncher ); Menu_AddItem( &s_controls.menu, &s_controls.lightning ); Menu_AddItem( &s_controls.menu, &s_controls.railgun ); Menu_AddItem( &s_controls.menu, &s_controls.plasma ); Menu_AddItem( &s_controls.menu, &s_controls.bfg ); Menu_AddItem( &s_controls.menu, &s_controls.grapple ); Menu_AddItem( &s_controls.menu, &s_controls.nailgun ); Menu_AddItem( &s_controls.menu, &s_controls.proxmine ); Menu_AddItem( &s_controls.menu, &s_controls.chaingun ); Menu_AddItem( &s_controls.menu, &s_controls.showscores ); Menu_AddItem( &s_controls.menu, &s_controls.useitem ); Menu_AddItem( &s_controls.menu, &s_controls.gesture ); Menu_AddItem( &s_controls.menu, &s_controls.chat ); Menu_AddItem( &s_controls.menu, &s_controls.chat2 ); Menu_AddItem( &s_controls.menu, &s_controls.chat3 ); Menu_AddItem( &s_controls.menu, &s_controls.chat4 ); Menu_AddItem( &s_controls.menu, &s_controls.voip_talk ); Menu_AddItem( &s_controls.menu, &s_controls.voip_teamonly ); Menu_AddItem( &s_controls.menu, &s_controls.back ); trap_Cvar_VariableStringBuffer( "name", s_controls.name.string, 16 ); Q_CleanStr( s_controls.name.string ); // initialize the configurable cvars Controls_InitCvars(); // initialize the current config Controls_GetConfig(); // intialize the model Controls_InitModel(); // intialize the weapons Controls_InitWeapons (); // initial default section s_controls.section = C_LOOKING; // update the ui Controls_Update(); } /* ================= Controls_Cache ================= */ void Controls_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); } /* ================= UI_ControlsMenu ================= */ void UI_ControlsMenu( void ) { Controls_MenuInit(); UI_PushMenu( &s_controls.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_firstconnect.c0000644000175000017500000003370611656310261020530 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" /****************************************************************************** * This is the menu displayed the first time the player selects "Multiplayer" * * It only has the must set or know about options! * * Options: Name, Network Rate, Delag, Auto Download * ******************************************************************************/ #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ID_NAME 10 #define ID_RATE 11 #define ID_DELAGHITSCAN 12 #define ID_ALLOWDOWNLOAD 13 #define ID_GO 100 #define ID_BACK 101 #define MAX_NAMELENGTH 20 /*static const char *rate_items[] = { "<= 28.8K", "33.6K", "56K", "ISDN", "LAN/Cable/xDSL", NULL };*/ extern const char *rate_items[]; static char* art_artlist[] = { ART_FRAMEL, ART_FRAMER, ART_BACK0, ART_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menubitmap_s go; menubitmap_s back; menutext_s info; menutext_s info2; //Important options menufield_s name; menulist_s rate; //Optional options menuradiobutton_s delaghitscan; menuradiobutton_s allowdownload; } firstconnect_t; static firstconnect_t s_firstconnect; /* ================= PlayerSettings_DrawName2 ================= */ static void PlayerSettings_DrawName2( void *self ) { menufield_s *f; qboolean focus; int style; char *txt; char c; float *color; int n; int basex, x, y; char name[32]; f = (menufield_s*)self; basex = f->generic.x; y = f->generic.y; focus = (f->generic.parent->cursor == f->generic.menuPosition); style = UI_LEFT|UI_SMALLFONT; color = text_color_normal; if( focus ) { style |= UI_PULSE; color = text_color_highlight; } UI_DrawProportionalString( basex, y, "Name:", style, color ); // draw the actual name basex += 64+10; //y += PROP_HEIGHT; y+= 4; txt = f->field.buffer; color = g_color_table[ColorIndex(COLOR_WHITE)]; x = basex; while ( (c = *txt) != 0 ) { if ( !focus && Q_IsColorString( txt ) ) { n = ColorIndex( *(txt+1) ); if( n == 0 ) { n = 7; } color = g_color_table[n]; txt += 2; continue; } UI_DrawChar( x, y, c, style, color ); txt++; x += SMALLCHAR_WIDTH; } // draw cursor if we have focus if( focus ) { if ( trap_Key_GetOverstrikeMode() ) { c = 11; } else { c = 10; } style &= ~UI_PULSE; style |= UI_BLINK; UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white ); } // draw at bottom also using proportional font Q_strncpyz( name, f->field.buffer, sizeof(name) ); Q_CleanStr( name ); } /* ================= FirstConnect_StatusBar_Name ================= */ static void FirstConnect_StatusBar_Name( void* ptr ) { UI_DrawString( 320, 440, "Your nickname", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= FirstConnect_StatusBar_Rate ================= */ static void FirstConnect_StatusBar_Rate( void* ptr ) { UI_DrawString( 320, 440, "Your connection speed", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= FirstConnect_StatusBar_Delag ================= */ static void FirstConnect_StatusBar_Delag( void* ptr ) { UI_DrawString( 320, 440, "Reliable Instanthit weapons", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= FirstConnect_StatusBar_Download ================= */ static void FirstConnect_StatusBar_Download( void* ptr ) { UI_DrawString( 320, 440, "Auto download missing maps and mods", UI_CENTER|UI_SMALLFONT, colorWhite ); } /* ================= FirstConnect_SaveChanges ================= */ static void FirstConnect_SaveChanges( void ) { // name trap_Cvar_Set( "name", s_firstconnect.name.field.buffer ); } /* ================= FirstConnect_Event ================= */ static void FirstConnect_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_GO: if (event != QM_ACTIVATED) break; FirstConnect_SaveChanges(); UI_PopMenu(); trap_Cvar_SetValue( "ui_setupchecked", 1 ); UI_ArenaServersMenu(); break; case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_RATE: if( s_firstconnect.rate.curvalue == 0 ) { trap_Cvar_SetValue( "rate", 2500 ); } else if( s_firstconnect.rate.curvalue == 1 ) { trap_Cvar_SetValue( "rate", 3000 ); } else if( s_firstconnect.rate.curvalue == 2 ) { trap_Cvar_SetValue( "rate", 4000 ); } else if( s_firstconnect.rate.curvalue == 3 ) { trap_Cvar_SetValue( "rate", 5000 ); } else if( s_firstconnect.rate.curvalue == 4 ) { trap_Cvar_SetValue( "rate", 25000 ); } break; case ID_ALLOWDOWNLOAD: trap_Cvar_SetValue( "cl_allowDownload", s_firstconnect.allowdownload.curvalue ); trap_Cvar_SetValue( "sv_allowDownload", s_firstconnect.allowdownload.curvalue ); break; case ID_DELAGHITSCAN: trap_Cvar_SetValue( "g_delagHitscan", s_firstconnect.delaghitscan.curvalue ); trap_Cvar_SetValue( "cg_delag", s_firstconnect.delaghitscan.curvalue ); break; } } /* ================= FirstConnect_SetMenuItems ================= */ static void FirstConnect_SetMenuItems( void ) { int rate; // name Q_strncpyz( s_firstconnect.name.field.buffer, UI_Cvar_VariableString("name"), sizeof(s_firstconnect.name.field.buffer) ); rate = trap_Cvar_VariableValue( "rate" ); if( rate <= 2500 ) { s_firstconnect.rate.curvalue = 0; } else if( rate <= 3000 ) { s_firstconnect.rate.curvalue = 1; } else if( rate <= 4000 ) { s_firstconnect.rate.curvalue = 2; } else if( rate <= 5000 ) { s_firstconnect.rate.curvalue = 3; } else { s_firstconnect.rate.curvalue = 4; } s_firstconnect.allowdownload.curvalue = trap_Cvar_VariableValue( "cl_allowDownload" ) != 0; s_firstconnect.delaghitscan.curvalue = trap_Cvar_VariableValue( "cg_delag" ) != 0; } /* ================= FirstConnect_MenuInit ================= */ void FirstConnect_MenuInit( void ) { int y; // zero set all our globals memset( &s_firstconnect, 0 ,sizeof(firstconnect_t) ); FirstConnect_Cache(); s_firstconnect.menu.wrapAround = qtrue; s_firstconnect.menu.fullscreen = qtrue; s_firstconnect.banner.generic.type = MTYPE_BTEXT; s_firstconnect.banner.generic.x = 320; s_firstconnect.banner.generic.y = 16; s_firstconnect.banner.string = "FIRST CONNECT"; s_firstconnect.banner.color = color_white; s_firstconnect.banner.style = UI_CENTER; s_firstconnect.framel.generic.type = MTYPE_BITMAP; s_firstconnect.framel.generic.name = ART_FRAMEL; s_firstconnect.framel.generic.flags = QMF_INACTIVE; s_firstconnect.framel.generic.x = 0; s_firstconnect.framel.generic.y = 78; s_firstconnect.framel.width = 256; s_firstconnect.framel.height = 329; s_firstconnect.framer.generic.type = MTYPE_BITMAP; s_firstconnect.framer.generic.name = ART_FRAMER; s_firstconnect.framer.generic.flags = QMF_INACTIVE; s_firstconnect.framer.generic.x = 376; s_firstconnect.framer.generic.y = 76; s_firstconnect.framer.width = 256; s_firstconnect.framer.height = 334; s_firstconnect.go.generic.type = MTYPE_BITMAP; s_firstconnect.go.generic.name = ART_FIGHT0; s_firstconnect.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_firstconnect.go.generic.callback = FirstConnect_Event; s_firstconnect.go.generic.id = ID_GO; s_firstconnect.go.generic.x = 640; s_firstconnect.go.generic.y = 480-64; s_firstconnect.go.width = 128; s_firstconnect.go.height = 64; s_firstconnect.go.focuspic = ART_FIGHT1; s_firstconnect.back.generic.type = MTYPE_BITMAP; s_firstconnect.back.generic.name = ART_BACK0; s_firstconnect.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_firstconnect.back.generic.callback = FirstConnect_Event; s_firstconnect.back.generic.id = ID_BACK; s_firstconnect.back.generic.x = 0; s_firstconnect.back.generic.y = 480-64; s_firstconnect.back.width = 128; s_firstconnect.back.height = 64; s_firstconnect.back.focuspic = ART_BACK1; y = 144; s_firstconnect.name.generic.type = MTYPE_FIELD; s_firstconnect.name.generic.flags = QMF_NODEFAULTINIT; s_firstconnect.name.generic.ownerdraw = PlayerSettings_DrawName2; s_firstconnect.name.field.widthInChars = MAX_NAMELENGTH; s_firstconnect.name.field.maxchars = MAX_NAMELENGTH; s_firstconnect.name.generic.x = 192; s_firstconnect.name.generic.y = y; s_firstconnect.name.generic.left = 192 - 8; s_firstconnect.name.generic.top = y - 8; s_firstconnect.name.generic.right = 192 + 200; s_firstconnect.name.generic.bottom = y + 2 * PROP_HEIGHT; s_firstconnect.name.generic.statusbar = FirstConnect_StatusBar_Name; y+= 4*PROP_HEIGHT; s_firstconnect.rate.generic.type = MTYPE_SPINCONTROL; s_firstconnect.rate.generic.name = "Data Rate:"; s_firstconnect.rate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_firstconnect.rate.generic.callback = FirstConnect_Event; s_firstconnect.rate.generic.id = ID_RATE; s_firstconnect.rate.generic.x = 320; s_firstconnect.rate.generic.y = y; s_firstconnect.rate.itemnames = rate_items; s_firstconnect.rate.generic.statusbar = FirstConnect_StatusBar_Rate; y += BIGCHAR_HEIGHT+2; s_firstconnect.delaghitscan.generic.type = MTYPE_RADIOBUTTON; s_firstconnect.delaghitscan.generic.name = "Compensate latency:"; s_firstconnect.delaghitscan.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_firstconnect.delaghitscan.generic.callback = FirstConnect_Event; s_firstconnect.delaghitscan.generic.id = ID_DELAGHITSCAN; s_firstconnect.delaghitscan.generic.x = 320; s_firstconnect.delaghitscan.generic.y = y; s_firstconnect.delaghitscan.generic.statusbar = FirstConnect_StatusBar_Delag; y += BIGCHAR_HEIGHT+2; s_firstconnect.allowdownload.generic.type = MTYPE_RADIOBUTTON; s_firstconnect.allowdownload.generic.name = "Automatic Downloading:"; s_firstconnect.allowdownload.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; s_firstconnect.allowdownload.generic.callback = FirstConnect_Event; s_firstconnect.allowdownload.generic.id = ID_ALLOWDOWNLOAD; s_firstconnect.allowdownload.generic.x = 320; s_firstconnect.allowdownload.generic.y = y; s_firstconnect.allowdownload.generic.statusbar = FirstConnect_StatusBar_Download; s_firstconnect.info.generic.type = MTYPE_TEXT; s_firstconnect.info.generic.x = 320; s_firstconnect.info.generic.y = 400; s_firstconnect.info.color = color_white; s_firstconnect.info.style = UI_CENTER|UI_SMALLFONT; s_firstconnect.info.string = "Note: All settings can be changed later in SETUP"; s_firstconnect.info2.generic.type = MTYPE_TEXT; s_firstconnect.info2.generic.x = 320; s_firstconnect.info2.generic.y = 80; s_firstconnect.info2.color = color_white; s_firstconnect.info2.style = UI_CENTER|UI_SMALLFONT; s_firstconnect.info2.string = "Please verify these settings"; Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.banner ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.framel ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.framer ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.go ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.back ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.name ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.rate ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.delaghitscan ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.allowdownload ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.info ); Menu_AddItem( &s_firstconnect.menu, &s_firstconnect.info2 ); FirstConnect_SetMenuItems(); } /* ================= FirstConnect_Cache ================= */ void FirstConnect_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!art_artlist[i]) break; trap_R_RegisterShaderNoMip(art_artlist[i]); } } /* ================= UI_FirstConnectMenu ================= */ void UI_FirstConnectMenu( void ) { FirstConnect_MenuInit(); UI_PushMenu( &s_firstconnect.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_kick.c0000644000175000017500000003003611656310261020663 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" #define ART_ARROWS "menu/art_blueish/arrows_vert_0" #define ART_ARROWUP "menu/art_blueish/arrows_vert_top" #define ART_ARROWDOWN "menu/art_blueish/arrows_vert_bot" #define ID_BACK 10 #define ID_GO 11 #define ID_LIST 12 #define ID_UP 13 #define ID_DOWN 14 #define ID_PLAYERNAME0 20 #define ID_PLAYERNAME1 21 #define ID_PLAYERNAME2 22 #define ID_PLAYERNAME3 23 #define ID_PLAYERNAME4 24 #define ID_PLAYERNAME5 25 #define ID_PLAYERNAME6 26 #define ID_PLAYERNAME7 27 #define ID_PLAYERNAME8 28 #define ID_PLAYERNAME9 29 #define SIZE_OF_LIST 10 //#define ID_PLAYERNAME10 30 #define SIZE_OF_NAME 32 #define VOTEKICK_MENU_VERTICAL_SPACING 20 typedef struct { int id; char name[SIZE_OF_NAME]; } player; typedef struct { menuframework_s menu; menubitmap_s arrows; menutext_s banner; menubitmap_s up; menubitmap_s down; menutext_s players[SIZE_OF_LIST]; menubitmap_s go; menubitmap_s back; int numPlayers; int selected; int startIndex; player players_profiles[MAX_CLIENTS]; } votemenu_kick_t; static votemenu_kick_t s_votemenu_kick; void UI_VoteKickMenuInternal( void ); static void populatePlayerList( void ) { int i; char playerinfo[MAX_INFO_STRING]; s_votemenu_kick.numPlayers = 0; for(i=0;iid) { case ID_BACK: if (event != QM_ACTIVATED) return; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } if(!s_votemenu_kick.selected) return; trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote clientkick %d",s_votemenu_kick.players_profiles[(s_votemenu_kick.selected-20)+s_votemenu_kick.startIndex].id) ); UI_PopMenu(); UI_PopMenu(); break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_kick.selected != ((menucommon_s*)ptr)->id) { s_votemenu_kick.selected = ((menucommon_s*)ptr)->id; UI_VoteKickMenuInternal(); } break; } } /* ================= UI_VoteKickMenu_UpEvent ================= */ static void UI_VoteKickMenu_UpEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( s_votemenu_kick.startIndex > 0 ) { s_votemenu_kick.startIndex--; } UI_VoteKickMenuInternal(); } /* ================= UI_VoteKickMenu_DownEvent ================= */ static void UI_VoteKickMenu_DownEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( s_votemenu_kick.startIndex+SIZE_OF_LIST < s_votemenu_kick.numPlayers ) { s_votemenu_kick.startIndex++; } UI_VoteKickMenuInternal(); } /* ================= UI_VoteKickMenu_Draw ================= */ static void UI_VoteKickMenu_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE KICK", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_kick.menu ); } /* ================= VoteKickMenu_Cache ================= */ static void VoteKickMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_BACKGROUND ); trap_R_RegisterShaderNoMip( ART_ARROWS ); trap_R_RegisterShaderNoMip( ART_ARROWUP ); trap_R_RegisterShaderNoMip( ART_ARROWDOWN ); } static void setKickMenutext(menutext_s *menu,int y,int id,qboolean active,char *text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!active) menu->generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu_kick.selected == id) menu->color = color_orange; menu->generic.x = 320; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteKickMenu_Event; menu->string = text; menu->style = UI_CENTER|UI_SMALLFONT; } /* ================= UI_VoteKickMenuInternal *Used then forcing a redraw ================= */ void UI_VoteKickMenuInternal( void ) { int y; s_votemenu_kick.menu.wrapAround = qtrue; s_votemenu_kick.menu.fullscreen = qfalse; s_votemenu_kick.menu.draw = UI_VoteKickMenu_Draw; s_votemenu_kick.banner.generic.type = MTYPE_BTEXT; s_votemenu_kick.banner.generic.x = 320; s_votemenu_kick.banner.generic.y = 16; s_votemenu_kick.banner.string = "CALL VOTE KICK"; s_votemenu_kick.banner.color = color_white; s_votemenu_kick.banner.style = UI_CENTER; s_votemenu_kick.arrows.generic.type = MTYPE_BITMAP; s_votemenu_kick.arrows.generic.name = ART_ARROWS; s_votemenu_kick.arrows.generic.flags = QMF_INACTIVE; s_votemenu_kick.arrows.generic.x = 200; s_votemenu_kick.arrows.generic.y = 128; s_votemenu_kick.arrows.width = 64; s_votemenu_kick.arrows.height = 128; y = 98; setKickMenutext(&s_votemenu_kick.players[0],y,ID_PLAYERNAME0,s_votemenu_kick.startIndexid - ID_BOTNAME0; addBotsMenuInfo.bots[addBotsMenuInfo.selectedBotNum].color = color_white; } /* ================= UI_AddBotsMenu_BackEvent ================= */ static void UI_AddBotsMenu_BackEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } UI_PopMenu(); } /* ================= UI_AddBotsMenu_SetBotNames ================= */ static void UI_AddBotsMenu_SetBotNames( void ) { int n; const char *info; for ( n = 0; n < 7; n++ ) { info = UI_GetBotInfoByNumber( addBotsMenuInfo.sortedBotNums[addBotsMenuInfo.baseBotNum + n] ); Q_strncpyz( addBotsMenuInfo.botnames[n], Info_ValueForKey( info, "name" ), sizeof(addBotsMenuInfo.botnames[n]) ); } } /* ================= UI_AddBotsMenu_UpEvent ================= */ static void UI_AddBotsMenu_UpEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( addBotsMenuInfo.baseBotNum > 0 ) { addBotsMenuInfo.baseBotNum--; UI_AddBotsMenu_SetBotNames(); } } /* ================= UI_AddBotsMenu_DownEvent ================= */ static void UI_AddBotsMenu_DownEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( addBotsMenuInfo.baseBotNum + 7 < addBotsMenuInfo.numBots ) { addBotsMenuInfo.baseBotNum++; UI_AddBotsMenu_SetBotNames(); } } /* ================= UI_AddBotsMenu_GetSortedBotNums ================= */ static int QDECL UI_AddBotsMenu_SortCompare( const void *arg1, const void *arg2 ) { int num1, num2; const char *info1, *info2; const char *name1, *name2; num1 = *(int *)arg1; num2 = *(int *)arg2; info1 = UI_GetBotInfoByNumber( num1 ); info2 = UI_GetBotInfoByNumber( num2 ); name1 = Info_ValueForKey( info1, "name" ); name2 = Info_ValueForKey( info2, "name" ); return Q_stricmp( name1, name2 ); } static void UI_AddBotsMenu_GetSortedBotNums( void ) { int n; // initialize the array for( n = 0; n < addBotsMenuInfo.numBots; n++ ) { addBotsMenuInfo.sortedBotNums[n] = n; } qsort( addBotsMenuInfo.sortedBotNums, addBotsMenuInfo.numBots, sizeof(addBotsMenuInfo.sortedBotNums[0]), UI_AddBotsMenu_SortCompare ); } /* ================= UI_AddBotsMenu_Draw ================= */ static void UI_AddBotsMenu_Draw( void ) { UI_DrawBannerString( 320, 16, "ADD BOTS", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &addBotsMenuInfo.menu ); } /* ================= UI_AddBotsMenu_Init ================= */ static const char *skillNames[] = { "I Can Win", "Bring It On", "Hurt Me Plenty", "Hardcore", "Nightmare!", NULL }; static const char *teamNames1[] = { "Free", NULL }; static const char *teamNames2[] = { "Red", "Blue", NULL }; static void UI_AddBotsMenu_Init( void ) { int n; int y; int gametype; int count; char info[MAX_INFO_STRING]; trap_GetConfigString(CS_SERVERINFO, info, MAX_INFO_STRING); gametype = atoi( Info_ValueForKey( info,"g_gametype" ) ); memset( &addBotsMenuInfo, 0 ,sizeof(addBotsMenuInfo) ); addBotsMenuInfo.menu.draw = UI_AddBotsMenu_Draw; addBotsMenuInfo.menu.fullscreen = qfalse; addBotsMenuInfo.menu.wrapAround = qtrue; addBotsMenuInfo.delay = 1000; UI_AddBots_Cache(); addBotsMenuInfo.numBots = UI_GetNumBots(); count = addBotsMenuInfo.numBots < 7 ? addBotsMenuInfo.numBots : 7; addBotsMenuInfo.arrows.generic.type = MTYPE_BITMAP; addBotsMenuInfo.arrows.generic.name = ART_ARROWS; addBotsMenuInfo.arrows.generic.flags = QMF_INACTIVE; addBotsMenuInfo.arrows.generic.x = 200; addBotsMenuInfo.arrows.generic.y = 128; addBotsMenuInfo.arrows.width = 64; addBotsMenuInfo.arrows.height = 128; addBotsMenuInfo.up.generic.type = MTYPE_BITMAP; addBotsMenuInfo.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; addBotsMenuInfo.up.generic.x = 200; addBotsMenuInfo.up.generic.y = 128; addBotsMenuInfo.up.generic.id = ID_UP; addBotsMenuInfo.up.generic.callback = UI_AddBotsMenu_UpEvent; addBotsMenuInfo.up.width = 64; addBotsMenuInfo.up.height = 64; addBotsMenuInfo.up.focuspic = ART_ARROWUP; addBotsMenuInfo.down.generic.type = MTYPE_BITMAP; addBotsMenuInfo.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; addBotsMenuInfo.down.generic.x = 200; addBotsMenuInfo.down.generic.y = 128+64; addBotsMenuInfo.down.generic.id = ID_DOWN; addBotsMenuInfo.down.generic.callback = UI_AddBotsMenu_DownEvent; addBotsMenuInfo.down.width = 64; addBotsMenuInfo.down.height = 64; addBotsMenuInfo.down.focuspic = ART_ARROWDOWN; for( n = 0, y = 120; n < count; n++, y += 20 ) { addBotsMenuInfo.bots[n].generic.type = MTYPE_PTEXT; addBotsMenuInfo.bots[n].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; addBotsMenuInfo.bots[n].generic.id = ID_BOTNAME0 + n; addBotsMenuInfo.bots[n].generic.x = 320 - 56; addBotsMenuInfo.bots[n].generic.y = y; addBotsMenuInfo.bots[n].generic.callback = UI_AddBotsMenu_BotEvent; addBotsMenuInfo.bots[n].string = addBotsMenuInfo.botnames[n]; addBotsMenuInfo.bots[n].color = color_orange; addBotsMenuInfo.bots[n].style = UI_LEFT|UI_SMALLFONT; } y += 12; addBotsMenuInfo.skill.generic.type = MTYPE_SPINCONTROL; addBotsMenuInfo.skill.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; addBotsMenuInfo.skill.generic.x = 320; addBotsMenuInfo.skill.generic.y = y; addBotsMenuInfo.skill.generic.name = "Skill:"; addBotsMenuInfo.skill.generic.id = ID_SKILL; addBotsMenuInfo.skill.itemnames = skillNames; addBotsMenuInfo.skill.curvalue = Com_Clamp( 0, 4, (int)trap_Cvar_VariableValue( "g_spSkill" ) - 1 ); y += SMALLCHAR_HEIGHT; addBotsMenuInfo.team.generic.type = MTYPE_SPINCONTROL; addBotsMenuInfo.team.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; addBotsMenuInfo.team.generic.x = 320; addBotsMenuInfo.team.generic.y = y; addBotsMenuInfo.team.generic.name = "Team: "; addBotsMenuInfo.team.generic.id = ID_TEAM; if( gametype >= GT_TEAM && gametype!=GT_LMS) { addBotsMenuInfo.team.itemnames = teamNames2; } else { addBotsMenuInfo.team.itemnames = teamNames1; addBotsMenuInfo.team.generic.flags = QMF_GRAYED; } addBotsMenuInfo.go.generic.type = MTYPE_BITMAP; addBotsMenuInfo.go.generic.name = ART_FIGHT0; addBotsMenuInfo.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; addBotsMenuInfo.go.generic.id = ID_GO; addBotsMenuInfo.go.generic.callback = UI_AddBotsMenu_FightEvent; addBotsMenuInfo.go.generic.x = 320+128-128; addBotsMenuInfo.go.generic.y = 256+128-64; addBotsMenuInfo.go.width = 128; addBotsMenuInfo.go.height = 64; addBotsMenuInfo.go.focuspic = ART_FIGHT1; addBotsMenuInfo.back.generic.type = MTYPE_BITMAP; addBotsMenuInfo.back.generic.name = ART_BACK0; addBotsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; addBotsMenuInfo.back.generic.id = ID_BACK; addBotsMenuInfo.back.generic.callback = UI_AddBotsMenu_BackEvent; addBotsMenuInfo.back.generic.x = 320-128; addBotsMenuInfo.back.generic.y = 256+128-64; addBotsMenuInfo.back.width = 128; addBotsMenuInfo.back.height = 64; addBotsMenuInfo.back.focuspic = ART_BACK1; addBotsMenuInfo.baseBotNum = 0; addBotsMenuInfo.selectedBotNum = 0; addBotsMenuInfo.bots[0].color = color_white; UI_AddBotsMenu_GetSortedBotNums(); UI_AddBotsMenu_SetBotNames(); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.arrows ); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.up ); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.down ); for( n = 0; n < count; n++ ) { Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.bots[n] ); } Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.skill ); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.team ); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.go ); Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.back ); } /* ================= UI_AddBots_Cache ================= */ void UI_AddBots_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_BACKGROUND ); trap_R_RegisterShaderNoMip( ART_ARROWS ); trap_R_RegisterShaderNoMip( ART_ARROWUP ); trap_R_RegisterShaderNoMip( ART_ARROWDOWN ); } /* ================= UI_AddBotsMenu ================= */ void UI_AddBotsMenu( void ) { UI_AddBotsMenu_Init(); UI_PushMenu( &addBotsMenuInfo.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_saveconfig.c0000644000175000017500000001373111656310261020147 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= SAVE CONFIG MENU ============================================================================= */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_SAVE0 "menu/art_blueish/save_0" #define ART_SAVE1 "menu/art_blueish/save_1" #define ART_BACKGROUND "menu/art_blueish/cut_frame" #define ID_NAME 10 #define ID_BACK 11 #define ID_SAVE 12 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s background; menufield_s savename; menubitmap_s back; menubitmap_s save; } saveConfig_t; static saveConfig_t saveConfig; /* =============== UI_SaveConfigMenu_BackEvent =============== */ static void UI_SaveConfigMenu_BackEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); } /* =============== UI_SaveConfigMenu_SaveEvent =============== */ static void UI_SaveConfigMenu_SaveEvent( void *ptr, int event ) { char configname[MAX_QPATH]; if( event != QM_ACTIVATED ) { return; } if( !saveConfig.savename.field.buffer[0] ) { return; } COM_StripExtension(saveConfig.savename.field.buffer, configname, sizeof(configname)); trap_Cmd_ExecuteText( EXEC_APPEND, va( "writeconfig %s.cfg\n", configname ) ); UI_PopMenu(); } /* =============== UI_SaveConfigMenu_SavenameDraw =============== */ static void UI_SaveConfigMenu_SavenameDraw( void *self ) { menufield_s *f; int style; float *color; f = (menufield_s *)self; if( f == Menu_ItemAtCursor( &saveConfig.menu ) ) { style = UI_LEFT|UI_PULSE|UI_SMALLFONT; color = text_color_highlight; } else { style = UI_LEFT|UI_SMALLFONT; color = colorRed; } UI_DrawProportionalString( 320, 192, "Enter filename:", UI_CENTER|UI_SMALLFONT, color_orange ); UI_FillRect( f->generic.x, f->generic.y, f->field.widthInChars*SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, colorBlack ); MField_Draw( &f->field, f->generic.x, f->generic.y, style, color ); } /* ================= UI_SaveConfigMenu_Init ================= */ static void UI_SaveConfigMenu_Init( void ) { memset( &saveConfig, 0, sizeof(saveConfig) ); UI_SaveConfigMenu_Cache(); saveConfig.menu.wrapAround = qtrue; saveConfig.menu.fullscreen = qtrue; saveConfig.banner.generic.type = MTYPE_BTEXT; saveConfig.banner.generic.x = 320; saveConfig.banner.generic.y = 16; saveConfig.banner.string = "SAVE CONFIG"; saveConfig.banner.color = color_white; saveConfig.banner.style = UI_CENTER; saveConfig.background.generic.type = MTYPE_BITMAP; saveConfig.background.generic.name = ART_BACKGROUND; saveConfig.background.generic.flags = QMF_INACTIVE; saveConfig.background.generic.x = 142; saveConfig.background.generic.y = 118; saveConfig.background.width = 359; saveConfig.background.height = 256; saveConfig.savename.generic.type = MTYPE_FIELD; saveConfig.savename.generic.flags = QMF_NODEFAULTINIT|QMF_UPPERCASE; saveConfig.savename.generic.ownerdraw = UI_SaveConfigMenu_SavenameDraw; saveConfig.savename.field.widthInChars = 20; saveConfig.savename.field.maxchars = 20; saveConfig.savename.generic.x = 240; saveConfig.savename.generic.y = 155+72; saveConfig.savename.generic.left = 240; saveConfig.savename.generic.top = 155+72; saveConfig.savename.generic.right = 233 + 20*SMALLCHAR_WIDTH; saveConfig.savename.generic.bottom = 155+72 + SMALLCHAR_HEIGHT+2; saveConfig.back.generic.type = MTYPE_BITMAP; saveConfig.back.generic.name = ART_BACK0; saveConfig.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; saveConfig.back.generic.id = ID_BACK; saveConfig.back.generic.callback = UI_SaveConfigMenu_BackEvent; saveConfig.back.generic.x = 0; saveConfig.back.generic.y = 480-64; saveConfig.back.width = 128; saveConfig.back.height = 64; saveConfig.back.focuspic = ART_BACK1; saveConfig.save.generic.type = MTYPE_BITMAP; saveConfig.save.generic.name = ART_SAVE0; saveConfig.save.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; saveConfig.save.generic.id = ID_SAVE; saveConfig.save.generic.callback = UI_SaveConfigMenu_SaveEvent; saveConfig.save.generic.x = 640; saveConfig.save.generic.y = 480-64; saveConfig.save.width = 128; saveConfig.save.height = 64; saveConfig.save.focuspic = ART_SAVE1; Menu_AddItem( &saveConfig.menu, &saveConfig.banner ); Menu_AddItem( &saveConfig.menu, &saveConfig.background ); Menu_AddItem( &saveConfig.menu, &saveConfig.savename ); Menu_AddItem( &saveConfig.menu, &saveConfig.back ); Menu_AddItem( &saveConfig.menu, &saveConfig.save ); } /* ================= UI_SaveConfigMenu_Cache ================= */ void UI_SaveConfigMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_SAVE0 ); trap_R_RegisterShaderNoMip( ART_SAVE1 ); trap_R_RegisterShaderNoMip( ART_BACKGROUND ); } /* =============== UI_SaveConfigMenu =============== */ void UI_SaveConfigMenu( void ) { UI_SaveConfigMenu_Init(); UI_PushMenu( &saveConfig.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_spreset.c0000644000175000017500000001152211656310261017504 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* ======================================================================= RESET MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAME "menu/art/cut_frame" #define ID_NO 100 #define ID_YES 101 typedef struct { menuframework_s menu; menutext_s no; menutext_s yes; int slashX; } resetMenu_t; static resetMenu_t s_reset; /* ================= Reset_MenuEvent ================= */ void Reset_MenuEvent(void* ptr, int event) { if( event != QM_ACTIVATED ) { return; } UI_PopMenu(); if( ((menucommon_s*)ptr)->id == ID_NO ) { return; } // reset the game, pop the level menu and restart it so it updates UI_NewGame(); trap_Cvar_SetValue( "ui_spSelection", 0 ); UI_PopMenu(); UI_SPLevelMenu(); } /* ================= Reset_MenuKey ================= */ static sfxHandle_t Reset_MenuKey( int key ) { switch ( key ) { case K_LEFTARROW: case K_RIGHTARROW: key = K_TAB; break; case 'n': case 'N': Reset_MenuEvent( &s_reset.no, QM_ACTIVATED ); break; case 'y': case 'Y': Reset_MenuEvent( &s_reset.yes, QM_ACTIVATED ); break; } return Menu_DefaultKey( &s_reset.menu, key ); } /* ================= Reset_MenuDraw ================= */ static void Reset_MenuDraw( void ) { UI_DrawNamedPic( 142, 118, 359, 256, ART_FRAME ); UI_DrawProportionalString( 320, 194 + 10, "RESET GAME?", UI_CENTER|UI_INVERSE, color_red ); UI_DrawProportionalString( s_reset.slashX, 265, "/", UI_LEFT|UI_INVERSE, color_red ); Menu_Draw( &s_reset.menu ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This resets all of the", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "single player game variables.", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 2, "Do this only if you want to", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 3, "start over from the beginning.", UI_CENTER|UI_SMALLFONT, color_yellow ); } /* ================= Reset_Cache ================= */ void Reset_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAME ); } /* ================= UI_ResetMenu ================= */ void UI_ResetMenu(void) { uiClientState_t cstate; int n1, n2, n3; int l1, l2, l3; // zero set all our globals memset( &s_reset, 0, sizeof(s_reset) ); Reset_Cache(); n1 = UI_ProportionalStringWidth( "YES/NO" ); n2 = UI_ProportionalStringWidth( "YES" ) + PROP_GAP_WIDTH; n3 = UI_ProportionalStringWidth( "/" ) + PROP_GAP_WIDTH; l1 = 320 - ( n1 / 2 ); l2 = l1 + n2; l3 = l2 + n3; s_reset.slashX = l2; s_reset.menu.draw = Reset_MenuDraw; s_reset.menu.key = Reset_MenuKey; s_reset.menu.wrapAround = qtrue; trap_GetClientState( &cstate ); if ( cstate.connState >= CA_CONNECTED ) { // float on top of running game s_reset.menu.fullscreen = qfalse; } else { // game not running s_reset.menu.fullscreen = qtrue; } s_reset.yes.generic.type = MTYPE_PTEXT; s_reset.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_reset.yes.generic.callback = Reset_MenuEvent; s_reset.yes.generic.id = ID_YES; s_reset.yes.generic.x = l1; s_reset.yes.generic.y = 264; s_reset.yes.string = "YES"; s_reset.yes.color = color_red; s_reset.yes.style = UI_LEFT; s_reset.no.generic.type = MTYPE_PTEXT; s_reset.no.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_reset.no.generic.callback = Reset_MenuEvent; s_reset.no.generic.id = ID_NO; s_reset.no.generic.x = l3; s_reset.no.generic.y = 264; s_reset.no.string = "NO"; s_reset.no.color = color_red; s_reset.no.style = UI_LEFT; Menu_AddItem( &s_reset.menu, &s_reset.yes ); Menu_AddItem( &s_reset.menu, &s_reset.no ); UI_PushMenu( &s_reset.menu ); Menu_SetCursorToItem( &s_reset.menu, &s_reset.no ); } openarena_0.8.8.orig/code/q3_ui/ui_challenges.c0000644000175000017500000004265711656310261020141 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2008-2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #include "../game/challenges.h" //This is an ugly way of syncing to cgame but it is platform compatible #define PARTofUI 1 #include "../cgame/cg_challenges.c" #define ID_BACK 1 //Main menu: #define ID_GENERAL 100 #define ID_GAMETYPES 101 #define ID_WEAPONS 102 #define ID_AWARDS 103 #define ID_POWERUPS 104 #define ID_FFA 105 #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define MAX_ENTRIES 18 #define MAX_INT_AS_STRING 8 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s general; menutext_s gametypes; menutext_s weapons; menutext_s awards; menutext_s powerups; int numberOfEntries; menutext_s entry[MAX_ENTRIES]; menutext_s entryIntText[MAX_ENTRIES]; char entryIntString[MAX_ENTRIES][MAX_INT_AS_STRING]; int entryInt[MAX_ENTRIES]; menutext_s notice; menutext_s notice2; menubitmap_s back; } challenges_t; static challenges_t challenges; static int mainSelection; //This should only be accessed locally void UI_ChallengesLocal( void ); /* ================= UI_Challenges_Event ================= */ static void UI_Challenges_Event( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GENERAL: mainSelection = 0; UI_PopMenu(); UI_ChallengesLocal(); break; case ID_GAMETYPES: mainSelection = 1; UI_PopMenu(); UI_ChallengesLocal(); break; case ID_WEAPONS: mainSelection = 2; UI_PopMenu(); UI_ChallengesLocal(); break; case ID_AWARDS: mainSelection = 3; UI_PopMenu(); UI_ChallengesLocal(); break; case ID_POWERUPS: mainSelection = 4; UI_PopMenu(); UI_ChallengesLocal(); break; case ID_FFA: break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_Challenges_Cache =============== */ void UI_Challenges_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== UI_Challenges_Init =============== */ static void UI_Challenges_Init( void ) { int y,i; memset( &challenges, 0, sizeof(challenges) ); UI_DisplayOptionsMenu_Cache(); challenges.menu.wrapAround = qtrue; challenges.menu.fullscreen = qtrue; challenges.banner.generic.type = MTYPE_BTEXT; challenges.banner.generic.flags = QMF_CENTER_JUSTIFY; challenges.banner.generic.x = 320; challenges.banner.generic.y = 16; challenges.banner.string = "STATISTICS"; challenges.banner.color = color_white; challenges.banner.style = UI_CENTER; challenges.framel.generic.type = MTYPE_BITMAP; challenges.framel.generic.name = ART_FRAMEL; challenges.framel.generic.flags = QMF_INACTIVE; challenges.framel.generic.x = 0; challenges.framel.generic.y = 78; challenges.framel.width = 256; challenges.framel.height = 329; challenges.framer.generic.type = MTYPE_BITMAP; challenges.framer.generic.name = ART_FRAMER; challenges.framer.generic.flags = QMF_INACTIVE; challenges.framer.generic.x = 376; challenges.framer.generic.y = 76; challenges.framer.width = 256; challenges.framer.height = 334; challenges.general.generic.type = MTYPE_PTEXT; challenges.general.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.general.generic.id = ID_GENERAL; challenges.general.generic.callback = UI_Challenges_Event; challenges.general.generic.x = 216; challenges.general.generic.y = 240 - 1 * PROP_HEIGHT; challenges.general.string = "GENERAL"; challenges.general.style = UI_RIGHT; challenges.general.color = color_red; /*challenges.gametypes.generic.type = MTYPE_PTEXT; challenges.gametypes.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.gametypes.generic.id = ID_GAMETYPES; challenges.gametypes.generic.callback = UI_Challenges_Event; challenges.gametypes.generic.x = 216; challenges.gametypes.generic.y = 240 - PROP_HEIGHT; challenges.gametypes.string = "GAMETYPES"; challenges.gametypes.style = UI_RIGHT; challenges.gametypes.color = color_red;*/ challenges.weapons.generic.type = MTYPE_PTEXT; challenges.weapons.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.weapons.generic.id = ID_WEAPONS; challenges.weapons.generic.callback = UI_Challenges_Event; challenges.weapons.generic.x = 216; challenges.weapons.generic.y = 240; challenges.weapons.string = "WEAPONS"; challenges.weapons.style = UI_RIGHT; challenges.weapons.color = color_red; challenges.awards.generic.type = MTYPE_PTEXT; challenges.awards.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.awards.generic.id = ID_AWARDS; challenges.awards.generic.callback = UI_Challenges_Event; challenges.awards.generic.x = 216; challenges.awards.generic.y = 240 + PROP_HEIGHT; challenges.awards.string = "AWARDS"; challenges.awards.style = UI_RIGHT; challenges.awards.color = color_red; challenges.powerups.generic.type = MTYPE_PTEXT; challenges.powerups.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.powerups.generic.id = ID_POWERUPS; challenges.powerups.generic.callback = UI_Challenges_Event; challenges.powerups.generic.x = 216; challenges.powerups.generic.y = 240 + PROP_HEIGHT*2; challenges.powerups.string = "POWERUPS"; challenges.powerups.style = UI_RIGHT; challenges.powerups.color = color_red; challenges.notice.generic.type = MTYPE_TEXT; challenges.notice.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE|QMF_SMALLFONT; challenges.notice.generic.x = 160; challenges.notice.generic.y = 430; challenges.notice.string = "Only results against"; challenges.notice2.generic.type = MTYPE_TEXT; challenges.notice2.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE|QMF_SMALLFONT; challenges.notice2.generic.x = 160; challenges.notice2.generic.y = 430+PROP_HEIGHT-10; challenges.notice2.string = "humans are counted"; challenges.back.generic.type = MTYPE_BITMAP; challenges.back.generic.name = ART_BACK0; challenges.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; challenges.back.generic.callback = UI_Challenges_Event; challenges.back.generic.id = ID_BACK; challenges.back.generic.x = 0; challenges.back.generic.y = 480-64; challenges.back.width = 128; challenges.back.height = 64; challenges.back.focuspic = ART_BACK1; switch(mainSelection) { case 0: //generel challenges.entry[0].string = "Total kills:"; challenges.entry[1].string = "Total deaths:"; //challenges.entry[2].string = "Total games:"; challenges.numberOfEntries = 2; challenges.entryInt[0] = getChallenge(GENERAL_TOTALKILLS); challenges.entryInt[1] = getChallenge(GENERAL_TOTALDEATHS); //challenges.entryInt[2] = getChallenge(GENERAL_TOTALGAMES); challenges.general.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; break; case 1: //gametypes challenges.numberOfEntries = 0; challenges.gametypes.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; break; case 2: //weapons challenges.entry[0].string = "Gauntlet kills:"; challenges.entry[1].string = "Machinegun kills:"; challenges.entry[2].string = "Shotgun kills:"; challenges.entry[3].string = "Granade kills:"; challenges.entry[4].string = "Rocket kills:"; challenges.entry[5].string = "Lightning kills:"; challenges.entry[6].string = "Plasmagun kills:"; challenges.entry[7].string = "Railgun kills:"; challenges.entry[8].string = "Instant rail kills:"; challenges.entry[9].string = "BFG kills:"; challenges.entry[10].string = "Grapple kills:"; challenges.entry[11].string = "Chaingun kills:"; challenges.entry[12].string = "Nailgun kills:"; challenges.entry[13].string = "Proxy mine kills:"; challenges.entry[14].string = "Telefrags:"; challenges.entry[15].string = "Push kills:"; challenges.entry[16].string = "Crush kills:"; challenges.numberOfEntries = 17; challenges.entryInt[0] = getChallenge(WEAPON_GAUNTLET_KILLS); challenges.entryInt[1] = getChallenge(WEAPON_MACHINEGUN_KILLS); challenges.entryInt[2] = getChallenge(WEAPON_SHOTGUN_KILLS); challenges.entryInt[3] = getChallenge(WEAPON_GRANADE_KILLS); challenges.entryInt[4] = getChallenge(WEAPON_ROCKET_KILLS); challenges.entryInt[5] = getChallenge(WEAPON_LIGHTNING_KILLS); challenges.entryInt[6] = getChallenge(WEAPON_PLASMA_KILLS); challenges.entryInt[7] = getChallenge(WEAPON_RAIL_KILLS); challenges.entryInt[8] = getChallenge(WEAPON_INSTANT_RAIL_KILLS); challenges.entryInt[9] = getChallenge(WEAPON_BFG_KILLS); challenges.entryInt[10] = getChallenge(WEAPON_GRAPPLE_KILLS); challenges.entryInt[11] = getChallenge(WEAPON_CHAINGUN_KILLS); challenges.entryInt[12] = getChallenge(WEAPON_NAILGUN_KILLS); challenges.entryInt[13] = getChallenge(WEAPON_MINE_KILLS); challenges.entryInt[14] = getChallenge(WEAPON_TELEFRAG_KILLS); challenges.entryInt[15] = getChallenge(WEAPON_PUSH_KILLS); challenges.entryInt[16] = getChallenge(WEAPON_CRUSH_KILLS); challenges.weapons.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; break; case 3: //awards challenges.entry[0].string = "Gauntlet"; challenges.entry[1].string = "Impressive"; challenges.entry[2].string = "Excellent"; challenges.entry[3].string = "Capture"; challenges.entry[4].string = "Assist"; challenges.entry[5].string = "Defend"; challenges.numberOfEntries = 6; challenges.entryInt[0] = getChallenge(WEAPON_GAUNTLET_KILLS); challenges.entryInt[1] = getChallenge(AWARD_IMPRESSIVE); challenges.entryInt[2] = getChallenge(AWARD_EXCELLENT); challenges.entryInt[3] = getChallenge(AWARD_CAPTURE); challenges.entryInt[4] = getChallenge(AWARD_ASSIST); challenges.entryInt[5] = getChallenge(AWARD_DEFENCE); challenges.awards.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; break; case 4: challenges.entry[0].string = "Quad kills"; challenges.entry[1].string = "2 fast 4 U"; challenges.entry[2].string = "They didn't see me"; challenges.entry[3].string = "I'm flying"; challenges.entry[4].string = "Killing machine"; challenges.entry[5].string = "Counter Quad"; challenges.entry[6].string = "Not fast enough"; challenges.entry[7].string = "You cannot hide"; challenges.entry[8].string = "Fall deep"; challenges.entry[9].string = "Counter battlesuit"; challenges.entry[10].string = "Counter regen"; challenges.entry[11].string = "Counter multi"; challenges.numberOfEntries = 12; challenges.entryInt[0] = getChallenge(POWERUP_QUAD_KILL); challenges.entryInt[1] = getChallenge(POWERUP_SPEED_KILL); challenges.entryInt[2] = getChallenge(POWERUP_INVIS_KILL); challenges.entryInt[3] = getChallenge(POWERUP_FLIGHT_KILL); challenges.entryInt[4] = getChallenge(POWERUP_MULTI_KILL); challenges.entryInt[5] = getChallenge(POWERUP_COUNTER_QUAD); challenges.entryInt[6] = getChallenge(POWERUP_COUNTER_SPEED); challenges.entryInt[7] = getChallenge(POWERUP_COUNTER_INVIS); challenges.entryInt[8] = getChallenge(POWERUP_COUNTER_FLIGHT); challenges.entryInt[9] = getChallenge(POWERUP_COUNTER_ENVIR); challenges.entryInt[10] = getChallenge(POWERUP_COUNTER_REGEN); challenges.entryInt[11] = getChallenge(POWERUP_COUNTER_MULTI); challenges.powerups.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE; break; default: challenges.numberOfEntries = 0; }; //Now write the challenges y = 240 - (int)((((float)challenges.numberOfEntries)/2.0) * (float)(BIGCHAR_HEIGHT + 2)); for(i=0;iid ) { case ID_SINGLEPLAYER: UI_SPLevelMenu(); break; case ID_MULTIPLAYER: if(ui_setupchecked.integer) UI_ArenaServersMenu(); else UI_FirstConnectMenu(); break; case ID_SETUP: UI_SetupMenu(); break; case ID_DEMOS: UI_DemosMenu(); break; /*case ID_CINEMATICS: UI_CinematicsMenu(); break;*/ case ID_CHALLENGES: UI_Challenges(); break; case ID_MODS: UI_ModsMenu(); break; case ID_TEAMARENA: trap_Cvar_Set( "fs_game", "missionpack"); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); break; case ID_EXIT: //UI_ConfirmMenu( "EXIT GAME?", 0, MainMenu_ExitAction ); UI_CreditMenu(); break; } } /* =============== MainMenu_Cache =============== */ void MainMenu_Cache( void ) { s_main.bannerModel = trap_R_RegisterModel( MAIN_BANNER_MODEL ); } sfxHandle_t ErrorMessage_Key(int key) { trap_Cvar_Set( "com_errorMessage", "" ); UI_MainMenu(); return (menu_null_sound); } /* =============== Main_MenuDraw TTimo: this function is common to the main menu and errorMessage menu =============== */ static void Main_MenuDraw( void ) { refdef_t refdef; refEntity_t ent; vec3_t origin; vec3_t angles; float adjust; float x, y, w, h; vec4_t color = {0.2, 0.2, 1.0, 1}; // setup the refdef memset( &refdef, 0, sizeof( refdef ) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); x = 0; y = 0; w = 640; h = 120; UI_AdjustFrom640( &x, &y, &w, &h ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; adjust = 0; // JDC: Kenneth asked me to stop this 1.0 * sin( (float)uis.realtime / 1000 ); refdef.fov_x = 60 + adjust; refdef.fov_y = 19.6875 + adjust; refdef.time = uis.realtime; origin[0] = 300; origin[1] = 0; origin[2] = -32; trap_R_ClearScene(); // add the model memset( &ent, 0, sizeof(ent) ); adjust = 5.0 * sin( (float)uis.realtime / 5000 ); VectorSet( angles, 0, 180 + adjust, 0 ); AnglesToAxis( angles, ent.axis ); ent.hModel = s_main.bannerModel; VectorCopy( origin, ent.origin ); VectorCopy( origin, ent.lightingOrigin ); ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; VectorCopy( ent.origin, ent.oldorigin ); trap_R_AddRefEntityToScene( &ent ); trap_R_RenderScene( &refdef ); if (strlen(s_errorMessage.errorMessage)) { UI_DrawProportionalString_AutoWrapped( 320, 192, 600, 20, s_errorMessage.errorMessage, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); } else { // standard menu drawing Menu_Draw( &s_main.menu ); } UI_DrawProportionalString( 320, 372, "", UI_CENTER|UI_SMALLFONT, color ); UI_DrawString( 320, 400, "OpenArena(c) 2005-2012 OpenArena Team", UI_CENTER|UI_SMALLFONT, color ); UI_DrawString( 320, 414, "OpenArena comes with ABSOLUTELY NO WARRANTY; this is free software", UI_CENTER|UI_SMALLFONT, color ); UI_DrawString( 320, 428, "and you are welcome to redistribute it under certain conditions;", UI_CENTER|UI_SMALLFONT, color ); UI_DrawString( 320, 444, "read COPYING for details.", UI_CENTER|UI_SMALLFONT, color ); //Draw version. UI_DrawString( 640-40, 480-14, "^70.8.8", UI_SMALLFONT, color ); if((int)trap_Cvar_VariableValue("protocol")!=71) UI_DrawString( 0, 480-14, va("^7Protocol: %i",(int)trap_Cvar_VariableValue("protocol")), UI_SMALLFONT, color); } /* =============== UI_TeamArenaExists =============== */ static qboolean UI_TeamArenaExists( void ) { int numdirs; char dirlist[2048]; char *dirptr; char *descptr; int i; int dirlen; numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) ); dirptr = dirlist; for( i = 0; i < numdirs; i++ ) { dirlen = strlen( dirptr ) + 1; descptr = dirptr + dirlen; if (Q_stricmp(dirptr, "missionpack") == 0) { return qtrue; } dirptr += dirlen + strlen(descptr) + 1; } return qfalse; } /* =============== UI_MainMenu The main menu only comes up when not in a game, so make sure that the attract loop server is down and that local cinematics are killed =============== */ void UI_MainMenu( void ) { int y; qboolean teamArena = qfalse; int style = UI_CENTER | UI_DROPSHADOW; trap_Cvar_Set( "sv_killserver", "1" ); trap_Cvar_SetValue( "handicap", 100 ); //Reset handicap during server change, it must be ser per game memset( &s_main, 0 ,sizeof(mainmenu_t) ); memset( &s_errorMessage, 0 ,sizeof(errorMessage_t) ); // com_errorMessage would need that too MainMenu_Cache(); trap_Cvar_VariableStringBuffer( "com_errorMessage", s_errorMessage.errorMessage, sizeof(s_errorMessage.errorMessage) ); if (strlen(s_errorMessage.errorMessage)) { s_errorMessage.menu.draw = Main_MenuDraw; s_errorMessage.menu.key = ErrorMessage_Key; s_errorMessage.menu.fullscreen = qtrue; s_errorMessage.menu.wrapAround = qtrue; s_errorMessage.menu.showlogo = qtrue; trap_Key_SetCatcher( KEYCATCH_UI ); uis.menusp = 0; UI_PushMenu ( &s_errorMessage.menu ); return; } s_main.menu.draw = Main_MenuDraw; s_main.menu.fullscreen = qtrue; s_main.menu.wrapAround = qtrue; s_main.menu.showlogo = qtrue; y = 134; s_main.singleplayer.generic.type = MTYPE_PTEXT; s_main.singleplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.singleplayer.generic.x = 320; s_main.singleplayer.generic.y = y; s_main.singleplayer.generic.id = ID_SINGLEPLAYER; s_main.singleplayer.generic.callback = Main_MenuEvent; s_main.singleplayer.string = "SINGLE PLAYER"; s_main.singleplayer.color = color_red; s_main.singleplayer.style = style; y += MAIN_MENU_VERTICAL_SPACING; s_main.multiplayer.generic.type = MTYPE_PTEXT; s_main.multiplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.multiplayer.generic.x = 320; s_main.multiplayer.generic.y = y; s_main.multiplayer.generic.id = ID_MULTIPLAYER; s_main.multiplayer.generic.callback = Main_MenuEvent; s_main.multiplayer.string = "MULTIPLAYER"; s_main.multiplayer.color = color_red; s_main.multiplayer.style = style; y += MAIN_MENU_VERTICAL_SPACING; s_main.setup.generic.type = MTYPE_PTEXT; s_main.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.setup.generic.x = 320; s_main.setup.generic.y = y; s_main.setup.generic.id = ID_SETUP; s_main.setup.generic.callback = Main_MenuEvent; s_main.setup.string = "SETUP"; s_main.setup.color = color_red; s_main.setup.style = style; y += MAIN_MENU_VERTICAL_SPACING; s_main.demos.generic.type = MTYPE_PTEXT; s_main.demos.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.demos.generic.x = 320; s_main.demos.generic.y = y; s_main.demos.generic.id = ID_DEMOS; s_main.demos.generic.callback = Main_MenuEvent; s_main.demos.string = "DEMOS"; s_main.demos.color = color_red; s_main.demos.style = style; /*y += MAIN_MENU_VERTICAL_SPACING; s_main.cinematics.generic.type = MTYPE_PTEXT; s_main.cinematics.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.cinematics.generic.x = 320; s_main.cinematics.generic.y = y; s_main.cinematics.generic.id = ID_CINEMATICS; s_main.cinematics.generic.callback = Main_MenuEvent; s_main.cinematics.string = "CINEMATICS"; s_main.cinematics.color = color_red; s_main.cinematics.style = style;*/ y += MAIN_MENU_VERTICAL_SPACING; s_main.challenges.generic.type = MTYPE_PTEXT; s_main.challenges.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.challenges.generic.x = 320; s_main.challenges.generic.y = y; s_main.challenges.generic.id = ID_CHALLENGES; s_main.challenges.generic.callback = Main_MenuEvent; s_main.challenges.string = "STATISTICS"; s_main.challenges.color = color_red; s_main.challenges.style = style; if (UI_TeamArenaExists()) { teamArena = qtrue; y += MAIN_MENU_VERTICAL_SPACING; s_main.teamArena.generic.type = MTYPE_PTEXT; s_main.teamArena.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.teamArena.generic.x = 320; s_main.teamArena.generic.y = y; s_main.teamArena.generic.id = ID_TEAMARENA; s_main.teamArena.generic.callback = Main_MenuEvent; s_main.teamArena.string = "MISSION PACK"; s_main.teamArena.color = color_red; s_main.teamArena.style = style; } y += MAIN_MENU_VERTICAL_SPACING; s_main.mods.generic.type = MTYPE_PTEXT; s_main.mods.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.mods.generic.x = 320; s_main.mods.generic.y = y; s_main.mods.generic.id = ID_MODS; s_main.mods.generic.callback = Main_MenuEvent; s_main.mods.string = "MODS"; s_main.mods.color = color_red; s_main.mods.style = style; y += MAIN_MENU_VERTICAL_SPACING; s_main.exit.generic.type = MTYPE_PTEXT; s_main.exit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_main.exit.generic.x = 320; s_main.exit.generic.y = y; s_main.exit.generic.id = ID_EXIT; s_main.exit.generic.callback = Main_MenuEvent; s_main.exit.string = "EXIT"; s_main.exit.color = color_red; s_main.exit.style = style; Menu_AddItem( &s_main.menu, &s_main.singleplayer ); Menu_AddItem( &s_main.menu, &s_main.multiplayer ); Menu_AddItem( &s_main.menu, &s_main.setup ); Menu_AddItem( &s_main.menu, &s_main.demos ); //Menu_AddItem( &s_main.menu, &s_main.cinematics ); Menu_AddItem( &s_main.menu, &s_main.challenges ); if (teamArena) { Menu_AddItem( &s_main.menu, &s_main.teamArena ); } Menu_AddItem( &s_main.menu, &s_main.mods ); Menu_AddItem( &s_main.menu, &s_main.exit ); trap_Key_SetCatcher( KEYCATCH_UI ); uis.menusp = 0; UI_PushMenu ( &s_main.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_spskill.c0000644000175000017500000002605311656310261017505 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= SINGLE PLAYER SKILL MENU ============================================================================= */ #include "ui_local.h" #define ART_FRAME "menu/art_blueish/cut_frame" #define ART_BACK "menu/art_blueish/back_0.tga" #define ART_BACK_FOCUS "menu/art_blueish/back_1.tga" #define ART_FIGHT "menu/art_blueish/fight_0" #define ART_FIGHT_FOCUS "menu/art_blueish/fight_1" #define ART_MAP_COMPLETE1 "menu/art/level_complete1" #define ART_MAP_COMPLETE2 "menu/art/level_complete2" #define ART_MAP_COMPLETE3 "menu/art/level_complete3" #define ART_MAP_COMPLETE4 "menu/art/level_complete4" #define ART_MAP_COMPLETE5 "menu/art/level_complete5" #define ID_BABY 10 #define ID_EASY 11 #define ID_MEDIUM 12 #define ID_HARD 13 #define ID_NIGHTMARE 14 #define ID_BACK 15 #define ID_FIGHT 16 typedef struct { menuframework_s menu; menubitmap_s art_frame; menutext_s art_banner; menutext_s item_baby; menutext_s item_easy; menutext_s item_medium; menutext_s item_hard; menutext_s item_nightmare; menubitmap_s art_skillPic; menubitmap_s item_back; menubitmap_s item_fight; const char *arenaInfo; qhandle_t skillpics[5]; sfxHandle_t nightmareSound; sfxHandle_t silenceSound; } skillMenuInfo_t; static skillMenuInfo_t skillMenuInfo; static void SetSkillColor( int skill, vec4_t color ) { switch( skill ) { case 1: skillMenuInfo.item_baby.color = color; break; case 2: skillMenuInfo.item_easy.color = color; break; case 3: skillMenuInfo.item_medium.color = color; break; case 4: skillMenuInfo.item_hard.color = color; break; case 5: skillMenuInfo.item_nightmare.color = color; break; default: break; } } /* ================= UI_SPSkillMenu_SkillEvent ================= */ static void UI_SPSkillMenu_SkillEvent( void *ptr, int notification ) { int id; int skill; if (notification != QM_ACTIVATED) return; SetSkillColor( (int)trap_Cvar_VariableValue( "g_spSkill" ), color_red ); id = ((menucommon_s*)ptr)->id; skill = id - ID_BABY + 1; trap_Cvar_SetValue( "g_spSkill", skill ); SetSkillColor( skill, color_white ); skillMenuInfo.art_skillPic.shader = skillMenuInfo.skillpics[skill - 1]; if( id == ID_NIGHTMARE ) { trap_S_StartLocalSound( skillMenuInfo.nightmareSound, CHAN_ANNOUNCER ); } else { trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER ); } } /* ================= UI_SPSkillMenu_FightEvent ================= */ static void UI_SPSkillMenu_FightEvent( void *ptr, int notification ) { if (notification != QM_ACTIVATED) return; UI_SPArena_Start( skillMenuInfo.arenaInfo ); } /* ================= UI_SPSkillMenu_BackEvent ================= */ static void UI_SPSkillMenu_BackEvent( void* ptr, int notification ) { if (notification != QM_ACTIVATED) { return; } trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER ); UI_PopMenu(); } /* ================= UI_SPSkillMenu_Key ================= */ static sfxHandle_t UI_SPSkillMenu_Key( int key ) { if( key == K_MOUSE2 || key == K_ESCAPE ) { trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER ); } return Menu_DefaultKey( &skillMenuInfo.menu, key ); } /* ================= UI_SPSkillMenu_Cache ================= */ void UI_SPSkillMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAME ); trap_R_RegisterShaderNoMip( ART_BACK ); trap_R_RegisterShaderNoMip( ART_BACK_FOCUS ); trap_R_RegisterShaderNoMip( ART_FIGHT ); trap_R_RegisterShaderNoMip( ART_FIGHT_FOCUS ); skillMenuInfo.skillpics[0] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 ); skillMenuInfo.skillpics[1] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 ); skillMenuInfo.skillpics[2] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 ); skillMenuInfo.skillpics[3] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 ); skillMenuInfo.skillpics[4] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 ); skillMenuInfo.nightmareSound = trap_S_RegisterSound( "sound/misc/nightmare.wav", qfalse ); skillMenuInfo.silenceSound = trap_S_RegisterSound( "sound/misc/silence.wav", qfalse ); } /* ================= UI_SPSkillMenu_Init ================= */ static void UI_SPSkillMenu_Init( void ) { int skill; memset( &skillMenuInfo, 0, sizeof(skillMenuInfo) ); skillMenuInfo.menu.fullscreen = qtrue; skillMenuInfo.menu.key = UI_SPSkillMenu_Key; UI_SPSkillMenu_Cache(); skillMenuInfo.art_frame.generic.type = MTYPE_BITMAP; skillMenuInfo.art_frame.generic.name = ART_FRAME; skillMenuInfo.art_frame.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; skillMenuInfo.art_frame.generic.x = 142; skillMenuInfo.art_frame.generic.y = 118; skillMenuInfo.art_frame.width = 359; skillMenuInfo.art_frame.height = 256; skillMenuInfo.art_banner.generic.type = MTYPE_BTEXT; skillMenuInfo.art_banner.generic.flags = QMF_CENTER_JUSTIFY; skillMenuInfo.art_banner.generic.x = 320; skillMenuInfo.art_banner.generic.y = 16; skillMenuInfo.art_banner.string = "DIFFICULTY"; skillMenuInfo.art_banner.color = color_white; skillMenuInfo.art_banner.style = UI_CENTER; skillMenuInfo.item_baby.generic.type = MTYPE_PTEXT; skillMenuInfo.item_baby.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_baby.generic.x = 320; skillMenuInfo.item_baby.generic.y = 170; skillMenuInfo.item_baby.generic.callback = UI_SPSkillMenu_SkillEvent; skillMenuInfo.item_baby.generic.id = ID_BABY; skillMenuInfo.item_baby.string = "I Can Win"; skillMenuInfo.item_baby.color = color_red; skillMenuInfo.item_baby.style = UI_CENTER; skillMenuInfo.item_easy.generic.type = MTYPE_PTEXT; skillMenuInfo.item_easy.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_easy.generic.x = 320; skillMenuInfo.item_easy.generic.y = 198; skillMenuInfo.item_easy.generic.callback = UI_SPSkillMenu_SkillEvent; skillMenuInfo.item_easy.generic.id = ID_EASY; skillMenuInfo.item_easy.string = "Bring It On"; skillMenuInfo.item_easy.color = color_red; skillMenuInfo.item_easy.style = UI_CENTER; skillMenuInfo.item_medium.generic.type = MTYPE_PTEXT; skillMenuInfo.item_medium.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_medium.generic.x = 320; skillMenuInfo.item_medium.generic.y = 227; skillMenuInfo.item_medium.generic.callback = UI_SPSkillMenu_SkillEvent; skillMenuInfo.item_medium.generic.id = ID_MEDIUM; skillMenuInfo.item_medium.string = "Hurt Me Plenty"; skillMenuInfo.item_medium.color = color_red; skillMenuInfo.item_medium.style = UI_CENTER; skillMenuInfo.item_hard.generic.type = MTYPE_PTEXT; skillMenuInfo.item_hard.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_hard.generic.x = 320; skillMenuInfo.item_hard.generic.y = 255; skillMenuInfo.item_hard.generic.callback = UI_SPSkillMenu_SkillEvent; skillMenuInfo.item_hard.generic.id = ID_HARD; skillMenuInfo.item_hard.string = "Hardcore"; skillMenuInfo.item_hard.color = color_red; skillMenuInfo.item_hard.style = UI_CENTER; skillMenuInfo.item_nightmare.generic.type = MTYPE_PTEXT; skillMenuInfo.item_nightmare.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_nightmare.generic.x = 320; skillMenuInfo.item_nightmare.generic.y = 283; skillMenuInfo.item_nightmare.generic.callback = UI_SPSkillMenu_SkillEvent; skillMenuInfo.item_nightmare.generic.id = ID_NIGHTMARE; skillMenuInfo.item_nightmare.string = "NIGHTMARE!"; skillMenuInfo.item_nightmare.color = color_red; skillMenuInfo.item_nightmare.style = UI_CENTER; skillMenuInfo.item_back.generic.type = MTYPE_BITMAP; skillMenuInfo.item_back.generic.name = ART_BACK; skillMenuInfo.item_back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_back.generic.x = 0; skillMenuInfo.item_back.generic.y = 480-64; skillMenuInfo.item_back.generic.callback = UI_SPSkillMenu_BackEvent; skillMenuInfo.item_back.generic.id = ID_BACK; skillMenuInfo.item_back.width = 128; skillMenuInfo.item_back.height = 64; skillMenuInfo.item_back.focuspic = ART_BACK_FOCUS; skillMenuInfo.art_skillPic.generic.type = MTYPE_BITMAP; skillMenuInfo.art_skillPic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; skillMenuInfo.art_skillPic.generic.x = 320-64; skillMenuInfo.art_skillPic.generic.y = 368; skillMenuInfo.art_skillPic.width = 128; skillMenuInfo.art_skillPic.height = 96; skillMenuInfo.item_fight.generic.type = MTYPE_BITMAP; skillMenuInfo.item_fight.generic.name = ART_FIGHT; skillMenuInfo.item_fight.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; skillMenuInfo.item_fight.generic.callback = UI_SPSkillMenu_FightEvent; skillMenuInfo.item_fight.generic.id = ID_FIGHT; skillMenuInfo.item_fight.generic.x = 640; skillMenuInfo.item_fight.generic.y = 480-64; skillMenuInfo.item_fight.width = 128; skillMenuInfo.item_fight.height = 64; skillMenuInfo.item_fight.focuspic = ART_FIGHT_FOCUS; Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_frame ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_banner ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_baby ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_easy ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_medium ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_hard ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_nightmare ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_skillPic ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_back ); Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_fight ); skill = (int)Com_Clamp( 1, 5, trap_Cvar_VariableValue( "g_spSkill" ) ); SetSkillColor( skill, color_white ); skillMenuInfo.art_skillPic.shader = skillMenuInfo.skillpics[skill - 1]; if( skill == 5 ) { trap_S_StartLocalSound( skillMenuInfo.nightmareSound, CHAN_ANNOUNCER ); } } void UI_SPSkillMenu( const char *arenaInfo ) { UI_SPSkillMenu_Init(); skillMenuInfo.arenaInfo = arenaInfo; UI_PushMenu( &skillMenuInfo.menu ); Menu_SetCursorToItem( &skillMenuInfo.menu, &skillMenuInfo.item_fight ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_custom.c0000644000175000017500000001610311656310261021253 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define VOTEMENU_BACK0 "menu/art_blueish/back_0" #define VOTEMENU_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" static char* votemenu_custom_artlist[] = { VOTEMENU_BACK0, VOTEMENU_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; #define ID_BACK 100 #define ID_GO 101 #define ID_CUSTOM0 102 //next 11 for ID_CUSTOM1-CUSTOM11 #define CUSTOM_MENU_VERTICAL_SPACING 19 #define CUSTOM_MENU_MAX_ENTRIES 12 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s go; //Buttons: menutext_s bEntry[CUSTOM_MENU_MAX_ENTRIES]; //The text: char text[CUSTOM_MENU_MAX_ENTRIES][32]; int selection; } votemenu_t; static votemenu_t s_votemenu_custom; void UI_VoteCustomMenuInternal( void ); /* ================= VoteMenu_custom_Event ================= */ static void VoteMenu_custom_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED || !s_votemenu_custom.selection) { return; } trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote custom %s",s_votemenu_custom.bEntry[s_votemenu_custom.selection-ID_CUSTOM0].string ) ); UI_PopMenu(); UI_PopMenu(); break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_custom.selection != ((menucommon_s*)ptr)->id) { s_votemenu_custom.selection = ((menucommon_s*)ptr)->id; UI_VoteCustomMenuInternal(); } break; } } static void setCustomMenutext(menutext_s *menu,int y,int id,char *text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(strlen(text)<1) menu->generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu_custom.selection == id) menu->color = color_orange; menu->generic.x = 320; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteMenu_custom_Event; menu->string = text; menu->style = UI_CENTER|UI_SMALLFONT; } /* ================= VoteMenu_Custom_Cache ================= */ static void VoteMenu_Custom_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!votemenu_custom_artlist[i]) break; trap_R_RegisterShaderNoMip(votemenu_custom_artlist[i]); } } /* ================= UI_VoteMenu_Custom_Draw ================= */ static void UI_VoteMenu_Custom_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE CUSTOM", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_custom.menu ); } /* ================= UI_VoteCustomMenuInternal *Used then forcing a redraw ================= */ void UI_VoteCustomMenuInternal( void ) { int y,i; char custominfo[MAX_INFO_STRING], *token,*pointer; VoteMenu_Custom_Cache(); memset( &custominfo, 0, sizeof(custominfo)); trap_Cvar_VariableStringBuffer("cg_vote_custom_commands",custominfo,sizeof(custominfo)); s_votemenu_custom.menu.wrapAround = qtrue; s_votemenu_custom.menu.fullscreen = qfalse; s_votemenu_custom.menu.draw = UI_VoteMenu_Custom_Draw; s_votemenu_custom.banner.generic.type = MTYPE_BTEXT; s_votemenu_custom.banner.generic.x = 320; s_votemenu_custom.banner.generic.y = 16; s_votemenu_custom.banner.string = "CALL VOTE CUSTOM"; s_votemenu_custom.banner.color = color_white; s_votemenu_custom.banner.style = UI_CENTER; pointer = custominfo; y = 98; for(i=0;iid - ID_CIN_IDLOGO; trap_Cvar_Set( "nextmap", va( "ui_cinematics %i", n ) ); if( uis.demoversion && ((menucommon_s*)ptr)->id == ID_CIN_END ) { trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic demoEnd.RoQ 1\n" ); } else { trap_Cmd_ExecuteText( EXEC_APPEND, va( "disconnect; cinematic %s.RoQ\n", cinematics[n] ) ); } } /* =============== UI_CinematicsMenu_Init =============== */ static void UI_CinematicsMenu_Init( void ) { int y; UI_CinematicsMenu_Cache(); memset( &cinematicsMenuInfo, 0, sizeof(cinematicsMenuInfo) ); cinematicsMenuInfo.menu.fullscreen = qtrue; cinematicsMenuInfo.banner.generic.type = MTYPE_BTEXT; cinematicsMenuInfo.banner.generic.x = 320; cinematicsMenuInfo.banner.generic.y = 16; cinematicsMenuInfo.banner.string = "CINEMATICS"; cinematicsMenuInfo.banner.color = color_white; cinematicsMenuInfo.banner.style = UI_CENTER; cinematicsMenuInfo.framel.generic.type = MTYPE_BITMAP; cinematicsMenuInfo.framel.generic.name = ART_FRAMEL; cinematicsMenuInfo.framel.generic.flags = QMF_INACTIVE; cinematicsMenuInfo.framel.generic.x = 0; cinematicsMenuInfo.framel.generic.y = 78; cinematicsMenuInfo.framel.width = 256; cinematicsMenuInfo.framel.height = 329; cinematicsMenuInfo.framer.generic.type = MTYPE_BITMAP; cinematicsMenuInfo.framer.generic.name = ART_FRAMER; cinematicsMenuInfo.framer.generic.flags = QMF_INACTIVE; cinematicsMenuInfo.framer.generic.x = 376; cinematicsMenuInfo.framer.generic.y = 76; cinematicsMenuInfo.framer.width = 256; cinematicsMenuInfo.framer.height = 334; y = 100; cinematicsMenuInfo.cin_idlogo.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_idlogo.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_idlogo.generic.x = 320; cinematicsMenuInfo.cin_idlogo.generic.y = y; cinematicsMenuInfo.cin_idlogo.generic.id = ID_CIN_IDLOGO; cinematicsMenuInfo.cin_idlogo.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_idlogo.string = "ID LOGO"; cinematicsMenuInfo.cin_idlogo.color = color_red; cinematicsMenuInfo.cin_idlogo.style = UI_CENTER; y += VERTICAL_SPACING; cinematicsMenuInfo.cin_intro.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_intro.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_intro.generic.x = 320; cinematicsMenuInfo.cin_intro.generic.y = y; cinematicsMenuInfo.cin_intro.generic.id = ID_CIN_INTRO; cinematicsMenuInfo.cin_intro.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_intro.string = "INTRO"; cinematicsMenuInfo.cin_intro.color = color_red; cinematicsMenuInfo.cin_intro.style = UI_CENTER; if( uis.demoversion ) { cinematicsMenuInfo.cin_intro.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier1.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier1.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier1.generic.x = 320; cinematicsMenuInfo.cin_tier1.generic.y = y; cinematicsMenuInfo.cin_tier1.generic.id = ID_CIN_TIER1; cinematicsMenuInfo.cin_tier1.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier1.string = "Tier 1"; cinematicsMenuInfo.cin_tier1.color = color_red; cinematicsMenuInfo.cin_tier1.style = UI_CENTER; if( !UI_CanShowTierVideo( 1 ) ) { cinematicsMenuInfo.cin_tier1.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier2.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier2.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier2.generic.x = 320; cinematicsMenuInfo.cin_tier2.generic.y = y; cinematicsMenuInfo.cin_tier2.generic.id = ID_CIN_TIER2; cinematicsMenuInfo.cin_tier2.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier2.string = "Tier 2"; cinematicsMenuInfo.cin_tier2.color = color_red; cinematicsMenuInfo.cin_tier2.style = UI_CENTER; if( !UI_CanShowTierVideo( 2 ) ) { cinematicsMenuInfo.cin_tier2.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier3.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier3.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier3.generic.x = 320; cinematicsMenuInfo.cin_tier3.generic.y = y; cinematicsMenuInfo.cin_tier3.generic.id = ID_CIN_TIER3; cinematicsMenuInfo.cin_tier3.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier3.string = "Tier 3"; cinematicsMenuInfo.cin_tier3.color = color_red; cinematicsMenuInfo.cin_tier3.style = UI_CENTER; if( !UI_CanShowTierVideo( 3 ) ) { cinematicsMenuInfo.cin_tier3.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier4.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier4.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier4.generic.x = 320; cinematicsMenuInfo.cin_tier4.generic.y = y; cinematicsMenuInfo.cin_tier4.generic.id = ID_CIN_TIER4; cinematicsMenuInfo.cin_tier4.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier4.string = "Tier 4"; cinematicsMenuInfo.cin_tier4.color = color_red; cinematicsMenuInfo.cin_tier4.style = UI_CENTER; if( !UI_CanShowTierVideo( 4 ) ) { cinematicsMenuInfo.cin_tier4.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier5.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier5.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier5.generic.x = 320; cinematicsMenuInfo.cin_tier5.generic.y = y; cinematicsMenuInfo.cin_tier5.generic.id = ID_CIN_TIER5; cinematicsMenuInfo.cin_tier5.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier5.string = "Tier 5"; cinematicsMenuInfo.cin_tier5.color = color_red; cinematicsMenuInfo.cin_tier5.style = UI_CENTER; if( !UI_CanShowTierVideo( 5 ) ) { cinematicsMenuInfo.cin_tier5.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier6.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier6.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier6.generic.x = 320; cinematicsMenuInfo.cin_tier6.generic.y = y; cinematicsMenuInfo.cin_tier6.generic.id = ID_CIN_TIER6; cinematicsMenuInfo.cin_tier6.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier6.string = "Tier 6"; cinematicsMenuInfo.cin_tier6.color = color_red; cinematicsMenuInfo.cin_tier6.style = UI_CENTER; if( !UI_CanShowTierVideo( 6 ) ) { cinematicsMenuInfo.cin_tier6.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_tier7.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_tier7.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_tier7.generic.x = 320; cinematicsMenuInfo.cin_tier7.generic.y = y; cinematicsMenuInfo.cin_tier7.generic.id = ID_CIN_TIER7; cinematicsMenuInfo.cin_tier7.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_tier7.string = "Tier 7"; cinematicsMenuInfo.cin_tier7.color = color_red; cinematicsMenuInfo.cin_tier7.style = UI_CENTER; if( !UI_CanShowTierVideo( 7 ) ) { cinematicsMenuInfo.cin_tier7.generic.flags |= QMF_GRAYED; } y += VERTICAL_SPACING; cinematicsMenuInfo.cin_end.generic.type = MTYPE_PTEXT; cinematicsMenuInfo.cin_end.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.cin_end.generic.x = 320; cinematicsMenuInfo.cin_end.generic.y = y; cinematicsMenuInfo.cin_end.generic.id = ID_CIN_END; cinematicsMenuInfo.cin_end.generic.callback = UI_CinematicsMenu_Event; cinematicsMenuInfo.cin_end.string = "END"; cinematicsMenuInfo.cin_end.color = color_red; cinematicsMenuInfo.cin_end.style = UI_CENTER; if( !UI_CanShowTierVideo( 8 ) ) { cinematicsMenuInfo.cin_end.generic.flags |= QMF_GRAYED; } cinematicsMenuInfo.back.generic.type = MTYPE_BITMAP; cinematicsMenuInfo.back.generic.name = ART_BACK0; cinematicsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; cinematicsMenuInfo.back.generic.id = ID_BACK; cinematicsMenuInfo.back.generic.callback = UI_CinematicsMenu_BackEvent; cinematicsMenuInfo.back.generic.x = 0; cinematicsMenuInfo.back.generic.y = 480-64; cinematicsMenuInfo.back.width = 128; cinematicsMenuInfo.back.height = 64; cinematicsMenuInfo.back.focuspic = ART_BACK1; Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.banner ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.framel ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.framer ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_idlogo ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_intro ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier1 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier2 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier3 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier4 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier5 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier6 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier7 ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_end ); Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.back ); } /* ================= UI_CinematicsMenu_Cache ================= */ void UI_CinematicsMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); } /* =============== UI_CinematicsMenu =============== */ void UI_CinematicsMenu( void ) { UI_CinematicsMenu_Init(); UI_PushMenu( &cinematicsMenuInfo.menu ); } /* =============== UI_CinematicsMenu_f =============== */ void UI_CinematicsMenu_f( void ) { int n; n = atoi( UI_Argv( 1 ) ); UI_CinematicsMenu(); Menu_SetCursorToItem( &cinematicsMenuInfo.menu, cinematicsMenuInfo.menu.items[n + 3] ); } openarena_0.8.8.orig/code/q3_ui/ui_cdkey.c0000644000175000017500000001615111656310261017121 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= CD KEY MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAME "menu/art/cut_frame" #define ART_ACCEPT0 "menu/art/accept_0" #define ART_ACCEPT1 "menu/art/accept_1" #define ART_BACK0 "menu/art/back_0" #define ART_BACK1 "menu/art/back_1" #define ID_CDKEY 10 #define ID_ACCEPT 11 #define ID_BACK 12 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s frame; menufield_s cdkey; menubitmap_s accept; menubitmap_s back; } cdkeyMenuInfo_t; static cdkeyMenuInfo_t cdkeyMenuInfo; /* =============== UI_CDKeyMenu_Event =============== */ static void UI_CDKeyMenu_Event( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_ACCEPT: if( cdkeyMenuInfo.cdkey.field.buffer[0] ) { trap_SetCDKey( cdkeyMenuInfo.cdkey.field.buffer ); } UI_PopMenu(); break; case ID_BACK: UI_PopMenu(); break; } } /* ================= UI_CDKeyMenu_PreValidateKey ================= */ static int UI_CDKeyMenu_PreValidateKey( const char *key ) { char ch; if( strlen( key ) != 16 ) { return 1; } while( ( ch = *key++ ) ) { switch( ch ) { case '2': case '3': case '7': case 'a': case 'b': case 'c': case 'd': case 'g': case 'h': case 'j': case 'l': case 'p': case 'r': case 's': case 't': case 'w': continue; default: return -1; } } return 0; } /* ================= UI_CDKeyMenu_DrawKey ================= */ static void UI_CDKeyMenu_DrawKey( void *self ) { menufield_s *f; qboolean focus; int style; char c; float *color; int x, y; int val; f = (menufield_s *)self; focus = (f->generic.parent->cursor == f->generic.menuPosition); style = UI_LEFT; if( focus ) { color = color_yellow; } else { color = color_orange; } x = 320 - 8 * BIGCHAR_WIDTH; y = 240 - BIGCHAR_HEIGHT / 2; UI_FillRect( x, y, 16 * BIGCHAR_WIDTH, BIGCHAR_HEIGHT, listbar_color ); UI_DrawString( x, y, f->field.buffer, style, color ); // draw cursor if we have focus if( focus ) { if ( trap_Key_GetOverstrikeMode() ) { c = 11; } else { c = 10; } style &= ~UI_PULSE; style |= UI_BLINK; UI_DrawChar( x + f->field.cursor * BIGCHAR_WIDTH, y, c, style, color_white ); } val = UI_CDKeyMenu_PreValidateKey( f->field.buffer ); if( val == 1 ) { UI_DrawProportionalString( 320, 376, "Please enter your CD Key", UI_CENTER|UI_SMALLFONT, color_yellow ); } else if ( val == 0 ) { UI_DrawProportionalString( 320, 376, "The CD Key appears to be valid, thank you", UI_CENTER|UI_SMALLFONT, color_white ); } else { UI_DrawProportionalString( 320, 376, "The CD Key is not valid", UI_CENTER|UI_SMALLFONT, color_red ); } } /* =============== UI_CDKeyMenu_Init =============== */ static void UI_CDKeyMenu_Init( void ) { trap_Cvar_Set( "ui_cdkeychecked", "1" ); UI_CDKeyMenu_Cache(); memset( &cdkeyMenuInfo, 0, sizeof(cdkeyMenuInfo) ); cdkeyMenuInfo.menu.wrapAround = qtrue; cdkeyMenuInfo.menu.fullscreen = qtrue; cdkeyMenuInfo.banner.generic.type = MTYPE_BTEXT; cdkeyMenuInfo.banner.generic.x = 320; cdkeyMenuInfo.banner.generic.y = 16; cdkeyMenuInfo.banner.string = "CD KEY"; cdkeyMenuInfo.banner.color = color_white; cdkeyMenuInfo.banner.style = UI_CENTER; cdkeyMenuInfo.frame.generic.type = MTYPE_BITMAP; cdkeyMenuInfo.frame.generic.name = ART_FRAME; cdkeyMenuInfo.frame.generic.flags = QMF_INACTIVE; cdkeyMenuInfo.frame.generic.x = 142; cdkeyMenuInfo.frame.generic.y = 118; cdkeyMenuInfo.frame.width = 359; cdkeyMenuInfo.frame.height = 256; cdkeyMenuInfo.cdkey.generic.type = MTYPE_FIELD; cdkeyMenuInfo.cdkey.generic.name = "CD Key:"; cdkeyMenuInfo.cdkey.generic.flags = QMF_LOWERCASE; cdkeyMenuInfo.cdkey.generic.x = 320 - BIGCHAR_WIDTH * 2.5; cdkeyMenuInfo.cdkey.generic.y = 240 - BIGCHAR_HEIGHT / 2; cdkeyMenuInfo.cdkey.field.widthInChars = 16; cdkeyMenuInfo.cdkey.field.maxchars = 16; cdkeyMenuInfo.cdkey.generic.ownerdraw = UI_CDKeyMenu_DrawKey; cdkeyMenuInfo.accept.generic.type = MTYPE_BITMAP; cdkeyMenuInfo.accept.generic.name = ART_ACCEPT0; cdkeyMenuInfo.accept.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; cdkeyMenuInfo.accept.generic.id = ID_ACCEPT; cdkeyMenuInfo.accept.generic.callback = UI_CDKeyMenu_Event; cdkeyMenuInfo.accept.generic.x = 640; cdkeyMenuInfo.accept.generic.y = 480-64; cdkeyMenuInfo.accept.width = 128; cdkeyMenuInfo.accept.height = 64; cdkeyMenuInfo.accept.focuspic = ART_ACCEPT1; cdkeyMenuInfo.back.generic.type = MTYPE_BITMAP; cdkeyMenuInfo.back.generic.name = ART_BACK0; cdkeyMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; cdkeyMenuInfo.back.generic.id = ID_BACK; cdkeyMenuInfo.back.generic.callback = UI_CDKeyMenu_Event; cdkeyMenuInfo.back.generic.x = 0; cdkeyMenuInfo.back.generic.y = 480-64; cdkeyMenuInfo.back.width = 128; cdkeyMenuInfo.back.height = 64; cdkeyMenuInfo.back.focuspic = ART_BACK1; Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.banner ); Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.frame ); Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.cdkey ); Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.accept ); if( uis.menusp ) { Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.back ); } trap_GetCDKey( cdkeyMenuInfo.cdkey.field.buffer, cdkeyMenuInfo.cdkey.field.maxchars + 1 ); if( trap_VerifyCDKey( cdkeyMenuInfo.cdkey.field.buffer, NULL ) == qfalse ) { cdkeyMenuInfo.cdkey.field.buffer[0] = 0; } } /* ================= UI_CDKeyMenu_Cache ================= */ void UI_CDKeyMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_ACCEPT0 ); trap_R_RegisterShaderNoMip( ART_ACCEPT1 ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FRAME ); } /* =============== UI_CDKeyMenu =============== */ void UI_CDKeyMenu( void ) { UI_CDKeyMenu_Init(); UI_PushMenu( &cdkeyMenuInfo.menu ); } /* =============== UI_CDKeyMenu_f =============== */ void UI_CDKeyMenu_f( void ) { UI_CDKeyMenu(); } openarena_0.8.8.orig/code/q3_ui/ui_players.c0000644000175000017500000007250711656310261017510 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // ui_players.c #include "ui_local.h" #define UI_TIMER_GESTURE 2300 #define UI_TIMER_JUMP 1000 #define UI_TIMER_LAND 130 #define UI_TIMER_WEAPON_SWITCH 300 #define UI_TIMER_ATTACK 500 #define UI_TIMER_MUZZLE_FLASH 20 #define UI_TIMER_WEAPON_DELAY 250 #define JUMP_HEIGHT 56 #define SWINGSPEED 0.3f #define SPIN_SPEED 0.9f #define COAST_TIME 1000 static int dp_realtime; static float jumpHeight; /* =============== UI_PlayerInfo_SetWeapon =============== */ static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) { gitem_t * item; char path[MAX_QPATH]; pi->currentWeapon = weaponNum; tryagain: pi->realWeapon = weaponNum; pi->weaponModel = 0; pi->barrelModel = 0; pi->flashModel = 0; if ( weaponNum == WP_NONE ) { return; } for ( item = bg_itemlist + 1; item->classname ; item++ ) { if ( item->giType != IT_WEAPON ) { continue; } if ( item->giTag == weaponNum ) { break; } } if ( item->classname ) { pi->weaponModel = trap_R_RegisterModel( item->world_model[0] ); } if( pi->weaponModel == 0 ) { if( weaponNum == WP_MACHINEGUN ) { weaponNum = WP_NONE; goto tryagain; } weaponNum = WP_MACHINEGUN; goto tryagain; } if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) { strcpy( path, item->world_model[0] ); COM_StripExtension( path, path, sizeof(path) ); strcat( path, "_barrel.md3" ); pi->barrelModel = trap_R_RegisterModel( path ); } strcpy( path, item->world_model[0] ); COM_StripExtension( path, path, sizeof(path) ); strcat( path, "_flash.md3" ); pi->flashModel = trap_R_RegisterModel( path ); switch( weaponNum ) { case WP_GAUNTLET: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_MACHINEGUN: MAKERGB( pi->flashDlightColor, 1, 1, 0 ); break; case WP_SHOTGUN: MAKERGB( pi->flashDlightColor, 1, 1, 0 ); break; case WP_GRENADE_LAUNCHER: MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f ); break; case WP_ROCKET_LAUNCHER: MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 ); break; case WP_LIGHTNING: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_RAILGUN: MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 ); break; case WP_PLASMAGUN: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; case WP_BFG: MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 ); break; case WP_GRAPPLING_HOOK: MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); break; default: MAKERGB( pi->flashDlightColor, 1, 1, 1 ); break; } } /* =============== UI_ForceLegsAnim =============== */ static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) { pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( anim == LEGS_JUMP ) { pi->legsAnimationTimer = UI_TIMER_JUMP; } } /* =============== UI_SetLegsAnim =============== */ static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) { if ( pi->pendingLegsAnim ) { anim = pi->pendingLegsAnim; pi->pendingLegsAnim = 0; } UI_ForceLegsAnim( pi, anim ); } /* =============== UI_ForceTorsoAnim =============== */ static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) { pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( anim == TORSO_GESTURE ) { pi->torsoAnimationTimer = UI_TIMER_GESTURE; } if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) { pi->torsoAnimationTimer = UI_TIMER_ATTACK; } } /* =============== UI_SetTorsoAnim =============== */ static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) { if ( pi->pendingTorsoAnim ) { anim = pi->pendingTorsoAnim; pi->pendingTorsoAnim = 0; } UI_ForceTorsoAnim( pi, anim ); } /* =============== UI_TorsoSequencing =============== */ static void UI_TorsoSequencing( playerInfo_t *pi ) { int currentAnim; currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if ( pi->weapon != pi->currentWeapon ) { if ( currentAnim != TORSO_DROP ) { pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; UI_ForceTorsoAnim( pi, TORSO_DROP ); } } if ( pi->torsoAnimationTimer > 0 ) { return; } if( currentAnim == TORSO_GESTURE ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } if ( currentAnim == TORSO_DROP ) { UI_PlayerInfo_SetWeapon( pi, pi->weapon ); pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; UI_ForceTorsoAnim( pi, TORSO_RAISE ); return; } if ( currentAnim == TORSO_RAISE ) { UI_SetTorsoAnim( pi, TORSO_STAND ); return; } } /* =============== UI_LegsSequencing =============== */ static void UI_LegsSequencing( playerInfo_t *pi ) { int currentAnim; currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; if ( pi->legsAnimationTimer > 0 ) { if ( currentAnim == LEGS_JUMP ) { jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP ); } return; } if ( currentAnim == LEGS_JUMP ) { UI_ForceLegsAnim( pi, LEGS_LAND ); pi->legsAnimationTimer = UI_TIMER_LAND; jumpHeight = 0; return; } if ( currentAnim == LEGS_LAND ) { UI_SetLegsAnim( pi, LEGS_IDLE ); return; } } /* ====================== UI_PositionEntityOnTag ====================== */ static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, clipHandle_t parentModel, char *tagName ) { int i; orientation_t lerped; // lerp the tag trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // cast away const because of compiler problems MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis ); entity->backlerp = parent->backlerp; } /* ====================== UI_PositionRotatedEntityOnTag ====================== */ static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, clipHandle_t parentModel, char *tagName ) { int i; orientation_t lerped; vec3_t tempAxis[3]; // lerp the tag trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // cast away const because of compiler problems MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis ); MatrixMultiply( lerped.axis, tempAxis, entity->axis ); } /* =============== UI_SetLerpFrameAnimation =============== */ static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { animation_t *anim; lf->animationNumber = newAnimation; newAnimation &= ~ANIM_TOGGLEBIT; if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) { trap_Error( va("Bad animation number: %i", newAnimation) ); } anim = &ci->animations[ newAnimation ]; lf->animation = anim; lf->animationTime = lf->frameTime + anim->initialLerp; } /* =============== UI_RunLerpFrame =============== */ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { int f; animation_t *anim; // see if the animation sequence is switching if ( newAnimation != lf->animationNumber || !lf->animation ) { UI_SetLerpFrameAnimation( ci, lf, newAnimation ); } // if we have passed the current frame, move it to // oldFrame and calculate a new frame if ( dp_realtime >= lf->frameTime ) { lf->oldFrame = lf->frame; lf->oldFrameTime = lf->frameTime; // get the next frame based on the animation anim = lf->animation; if ( dp_realtime < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; if ( f >= anim->numFrames ) { f -= anim->numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { f = anim->numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = dp_realtime; } } lf->frame = anim->firstFrame + f; if ( dp_realtime > lf->frameTime ) { lf->frameTime = dp_realtime; } } if ( lf->frameTime > dp_realtime + 200 ) { lf->frameTime = dp_realtime; } if ( lf->oldFrameTime > dp_realtime ) { lf->oldFrameTime = dp_realtime; } // calculate current lerp value if ( lf->frameTime == lf->oldFrameTime ) { lf->backlerp = 0; } else { lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); } } /* =============== UI_PlayerAnimation =============== */ static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp, int *torsoOld, int *torso, float *torsoBackLerp ) { // legs animation pi->legsAnimationTimer -= uis.frametime; if ( pi->legsAnimationTimer < 0 ) { pi->legsAnimationTimer = 0; } UI_LegsSequencing( pi ); if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN ); } else { UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim ); } *legsOld = pi->legs.oldFrame; *legs = pi->legs.frame; *legsBackLerp = pi->legs.backlerp; // torso animation pi->torsoAnimationTimer -= uis.frametime; if ( pi->torsoAnimationTimer < 0 ) { pi->torsoAnimationTimer = 0; } UI_TorsoSequencing( pi ); UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim ); *torsoOld = pi->torso.oldFrame; *torso = pi->torso.frame; *torsoBackLerp = pi->torso.backlerp; } /* ================== UI_SwingAngles ================== */ static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qboolean *swinging ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = uis.frametime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = uis.frametime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } } /* ====================== UI_MovedirAdjustment ====================== */ static float UI_MovedirAdjustment( playerInfo_t *pi ) { vec3_t relativeAngles; vec3_t moveVector; VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles ); AngleVectors( relativeAngles, moveVector, NULL, NULL ); if ( Q_fabs( moveVector[0] ) < 0.01 ) { moveVector[0] = 0.0; } if ( Q_fabs( moveVector[1] ) < 0.01 ) { moveVector[1] = 0.0; } if ( moveVector[1] == 0 && moveVector[0] > 0 ) { return 0; } if ( moveVector[1] < 0 && moveVector[0] > 0 ) { return 22; } if ( moveVector[1] < 0 && moveVector[0] == 0 ) { return 45; } if ( moveVector[1] < 0 && moveVector[0] < 0 ) { return -22; } if ( moveVector[1] == 0 && moveVector[0] < 0 ) { return 0; } if ( moveVector[1] > 0 && moveVector[0] < 0 ) { return 22; } if ( moveVector[1] > 0 && moveVector[0] == 0 ) { return -45; } return -22; } /* =============== UI_PlayerAngles =============== */ static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { vec3_t legsAngles, torsoAngles, headAngles; float dest; float adjust; VectorCopy( pi->viewAngles, headAngles ); headAngles[YAW] = AngleMod( headAngles[YAW] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { // if not standing still, always point all in the same direction pi->torso.yawing = qtrue; // always center pi->torso.pitching = qtrue; // always center pi->legs.yawing = qtrue; // always center } // adjust legs for movement dir adjust = UI_MovedirAdjustment( pi ); legsAngles[YAW] = headAngles[YAW] + adjust; torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust; // torso UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing ); UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing ); torsoAngles[YAW] = pi->torso.yawAngle; legsAngles[YAW] = pi->legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[PITCH] > 180 ) { dest = (-360 + headAngles[PITCH]) * 0.75; } else { dest = headAngles[PITCH] * 0.75; } UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles[PITCH] = pi->torso.pitchAngle; // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesToAxis( legsAngles, legs ); AnglesToAxis( torsoAngles, torso ); AnglesToAxis( headAngles, head ); } /* =============== UI_PlayerFloatSprite =============== */ static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) { refEntity_t ent; memset( &ent, 0, sizeof( ent ) ); VectorCopy( origin, ent.origin ); ent.origin[2] += 48; ent.reType = RT_SPRITE; ent.customShader = shader; ent.radius = 10; ent.renderfx = 0; trap_R_AddRefEntityToScene( &ent ); } /* ====================== UI_MachinegunSpinAngle ====================== */ float UI_MachinegunSpinAngle( playerInfo_t *pi ) { int delta; float angle; float speed; int torsoAnim; delta = dp_realtime - pi->barrelTime; if ( pi->barrelSpinning ) { angle = pi->barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = pi->barrelAngle + delta * speed; } torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if( torsoAnim == TORSO_ATTACK2 ) { torsoAnim = TORSO_ATTACK; } if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) { pi->barrelTime = dp_realtime; pi->barrelAngle = AngleMod( angle ); pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK); } return angle; } /* =============== UI_DrawPlayer =============== */ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) { refdef_t refdef; refEntity_t legs; refEntity_t torso; refEntity_t head; refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t origin; int renderfx; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; float len; float xx; if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) { return; } dp_realtime = time; if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) { pi->weapon = pi->pendingWeapon; pi->lastWeapon = pi->pendingWeapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; if( pi->currentWeapon != pi->weapon ) { trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL ); } } UI_AdjustFrom640( &x, &y, &w, &h ); y -= jumpHeight; memset( &refdef, 0, sizeof( refdef ) ); memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); refdef.fov_y = atan2( refdef.height, xx ); refdef.fov_y *= ( 360 / M_PI ); // calculate distance so the player nearly fills the box len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); refdef.time = dp_realtime; trap_R_ClearScene(); // get the rotation information UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; // // add the legs // legs.hModel = pi->legsModel; legs.customSkin = pi->legsSkin; VectorCopy( origin, legs.origin ); VectorCopy( origin, legs.lightingOrigin ); legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); trap_R_AddRefEntityToScene( &legs ); if (!legs.hModel) { return; } // // add the torso // torso.hModel = pi->torsoModel; if (!torso.hModel) { return; } torso.customSkin = pi->torsoSkin; VectorCopy( origin, torso.lightingOrigin ); UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso"); torso.renderfx = renderfx; trap_R_AddRefEntityToScene( &torso ); // // add the head // head.hModel = pi->headModel; if (!head.hModel) { return; } head.customSkin = pi->headSkin; VectorCopy( origin, head.lightingOrigin ); UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head"); head.renderfx = renderfx; trap_R_AddRefEntityToScene( &head ); // // add the gun // if ( pi->currentWeapon != WP_NONE ) { memset( &gun, 0, sizeof(gun) ); gun.hModel = pi->weaponModel; VectorCopy( origin, gun.lightingOrigin ); UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); gun.renderfx = renderfx; trap_R_AddRefEntityToScene( &gun ); } // // add the spinning barrel // if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { vec3_t angles; memset( &barrel, 0, sizeof(barrel) ); VectorCopy( origin, barrel.lightingOrigin ); barrel.renderfx = renderfx; barrel.hModel = pi->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = UI_MachinegunSpinAngle( pi ); if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { angles[PITCH] = angles[ROLL]; angles[ROLL] = 0; } AnglesToAxis( angles, barrel.axis ); UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); trap_R_AddRefEntityToScene( &barrel ); } // // add muzzle flash // if ( dp_realtime <= pi->muzzleFlashTime ) { if ( pi->flashModel ) { memset( &flash, 0, sizeof(flash) ); flash.hModel = pi->flashModel; VectorCopy( origin, flash.lightingOrigin ); UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); flash.renderfx = renderfx; trap_R_AddRefEntityToScene( &flash ); } // make a dlight for the flash if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], pi->flashDlightColor[1], pi->flashDlightColor[2] ); } } // // add the chat icon // if ( pi->chat ) { UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); } // // add an accent light // origin[0] -= 100; // + = behind, - = in front origin[1] += 100; // + = left, - = right origin[2] += 100; // + = above, - = below trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 ); origin[0] -= 100; origin[1] -= 100; origin[2] -= 100; trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 ); trap_R_RenderScene( &refdef ); } /* ========================== UI_RegisterClientSkin ========================== */ static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) { char filename[MAX_QPATH]; Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName ); pi->legsSkin = trap_R_RegisterSkin( filename ); Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName ); pi->torsoSkin = trap_R_RegisterSkin( filename ); Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName ); pi->headSkin = trap_R_RegisterSkin( filename ); if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) { return qfalse; } return qtrue; } /* ====================== UI_ParseAnimationFile ====================== */ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) { char *text_p, *prev; int len; int i; char *token; float fps; int skip; char text[20000]; fileHandle_t f; memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS ); // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { return qfalse; } if ( len >= ( sizeof( text ) - 1 ) ) { Com_Printf( "File %s too long\n", filename ); trap_FS_FCloseFile( f ); return qfalse; } trap_FS_Read( text, len, f ); text[len] = 0; trap_FS_FCloseFile( f ); // parse the text text_p = text; skip = 0; // quite the compiler warning // read optional parameters while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } continue; } // if it is a number, start parsing animations if ( token[0] >= '0' && token[0] <= '9' ) { text_p = prev; // unget the token break; } Com_Printf( "unknown token '%s' is %s\n", token, filename ); } // read information for each frame for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].firstFrame = atoi( token ); // leg only frames are adjusted to not count the upper body only frames if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } if ( i >= LEGS_WALKCR ) { animations[i].firstFrame -= skip; } token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].numFrames = atoi( token ); token = COM_Parse( &text_p ); if ( !token ) { break; } animations[i].loopFrames = atoi( token ); token = COM_Parse( &text_p ); if ( !token ) { break; } fps = atof( token ); if ( fps == 0 ) { fps = 1; } animations[i].frameLerp = 1000 / fps; animations[i].initialLerp = 1000 / fps; } if ( i != MAX_ANIMATIONS ) { Com_Printf( "Error parsing animation file: %s\n", filename ); return qfalse; } return qtrue; } /* ========================== UI_RegisterClientModelname ========================== */ qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) { char modelName[MAX_QPATH]; char skinName[MAX_QPATH]; char filename[MAX_QPATH]; char *slash; pi->torsoModel = 0; pi->headModel = 0; if ( !modelSkinName[0] ) { return qfalse; } Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) ); slash = strchr( modelName, '/' ); if ( !slash ) { // modelName did not include a skin name Q_strncpyz( skinName, "default", sizeof( skinName ) ); } else { Q_strncpyz( skinName, slash + 1, sizeof( skinName ) ); // truncate modelName *slash = 0; } // load cmodels before models so filecache works Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); pi->legsModel = trap_R_RegisterModel( filename ); if ( !pi->legsModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); pi->torsoModel = trap_R_RegisterModel( filename ); if ( !pi->torsoModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName ); pi->headModel = trap_R_RegisterModel( filename ); if ( !pi->headModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } // if any skins failed to load, fall back to default if ( !UI_RegisterClientSkin( pi, modelName, skinName ) ) { if ( !UI_RegisterClientSkin( pi, modelName, "default" ) ) { Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName ); return qfalse; } } // load the animations Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); if ( !UI_ParseAnimationFile( filename, pi->animations ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } return qtrue; } /* =============== UI_PlayerInfo_SetModel =============== */ void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ) { memset( pi, 0, sizeof(*pi) ); UI_RegisterClientModelname( pi, model ); pi->weapon = WP_MACHINEGUN; pi->currentWeapon = pi->weapon; pi->lastWeapon = pi->weapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; pi->chat = qfalse; pi->newModel = qtrue; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); } /* =============== UI_PlayerInfo_SetInfo =============== */ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) { int currentAnim; weapon_t weaponNum; pi->chat = chat; // view angles VectorCopy( viewAngles, pi->viewAngles ); // move angles VectorCopy( moveAngles, pi->moveAngles ); if ( pi->newModel ) { pi->newModel = qfalse; jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); pi->legs.yawAngle = viewAngles[YAW]; pi->legs.yawing = qfalse; pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); pi->torso.yawAngle = viewAngles[YAW]; pi->torso.yawing = qfalse; if ( weaponNumber != -1 ) { pi->weapon = weaponNumber; pi->currentWeapon = weaponNumber; pi->lastWeapon = weaponNumber; pi->pendingWeapon = -1; pi->weaponTimer = 0; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); } return; } // weapon if ( weaponNumber == -1 ) { pi->pendingWeapon = -1; pi->weaponTimer = 0; } else if ( weaponNumber != WP_NONE ) { pi->pendingWeapon = weaponNumber; pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY; } weaponNum = pi->lastWeapon; pi->weapon = weaponNum; if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) { torsoAnim = legsAnim = BOTH_DEATH1; pi->weapon = pi->currentWeapon = WP_NONE; UI_PlayerInfo_SetWeapon( pi, pi->weapon ); jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); return; } // leg animation currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) { pi->pendingLegsAnim = legsAnim; } else if ( legsAnim != currentAnim ) { jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); } // torso animation if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) { if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { torsoAnim = TORSO_STAND2; } else { torsoAnim = TORSO_STAND; } } if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) { if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { torsoAnim = TORSO_ATTACK2; } else { torsoAnim = TORSO_ATTACK; } pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH; //FIXME play firing sound here } currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) { pi->pendingTorsoAnim = torsoAnim; } else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) { pi->pendingTorsoAnim = torsoAnim; } else if ( torsoAnim != currentAnim ) { pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); } } openarena_0.8.8.orig/code/q3_ui/ui_ingame.c0000644000175000017500000002565111656310261017267 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.SERVER Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= INGAME MENU ======================================================================= */ #include "ui_local.h" #define INGAME_FRAME "menu/art_blueish/addbotframe" //#define INGAME_FRAME "menu/art_blueish/cut_frame" #define INGAME_MENU_VERTICAL_SPACING 28 #define ID_TEAM 10 #define ID_ADDBOTS 11 #define ID_REMOVEBOTS 12 #define ID_SETUP 13 #define ID_SERVERINFO 14 #define ID_LEAVEARENA 15 #define ID_RESTART 16 #define ID_QUIT 17 #define ID_RESUME 18 #define ID_TEAMORDERS 19 #define ID_VOTE 20 typedef struct { menuframework_s menu; menubitmap_s frame; menutext_s team; menutext_s setup; menutext_s server; menutext_s leave; menutext_s restart; menutext_s addbots; menutext_s removebots; menutext_s teamorders; menutext_s quit; menutext_s resume; menutext_s vote; } ingamemenu_t; static ingamemenu_t s_ingame; /* ================= InGame_RestartAction ================= */ static void InGame_RestartAction( qboolean result ) { if( !result ) { return; } UI_PopMenu(); trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); } /* ================= InGame_QuitAction ================= */ static void InGame_QuitAction( qboolean result ) { if( !result ) { return; } UI_PopMenu(); //UI_CreditMenu(); trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } /* ================= InGame_Event ================= */ void InGame_Event( void *ptr, int notification ) { if( notification != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_TEAM: UI_TeamMainMenu(); break; case ID_SETUP: UI_SetupMenu(); break; case ID_LEAVEARENA: trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" ); break; case ID_RESTART: UI_ConfirmMenu( "RESTART ARENA?", 0, InGame_RestartAction ); break; case ID_QUIT: UI_ConfirmMenu( "EXIT GAME?", 0, InGame_QuitAction ); break; case ID_SERVERINFO: UI_ServerInfoMenu(); break; case ID_ADDBOTS: UI_AddBotsMenu(); break; case ID_REMOVEBOTS: UI_RemoveBotsMenu(); break; case ID_TEAMORDERS: UI_TeamOrdersMenu(); break; case ID_RESUME: UI_PopMenu(); break; case ID_VOTE: UI_VoteMenuMenu(); break; } } /* ================= InGame_MenuInit ================= */ void InGame_MenuInit( void ) { int y; uiClientState_t cs; char info[MAX_INFO_STRING]; int team; memset( &s_ingame, 0 ,sizeof(ingamemenu_t) ); InGame_Cache(); s_ingame.menu.wrapAround = qtrue; s_ingame.menu.fullscreen = qfalse; s_ingame.frame.generic.type = MTYPE_BITMAP; s_ingame.frame.generic.flags = QMF_INACTIVE; s_ingame.frame.generic.name = INGAME_FRAME; s_ingame.frame.generic.x = 320-233;//142; s_ingame.frame.generic.y = 240-166;//118; s_ingame.frame.width = 466;//359; s_ingame.frame.height = 332;//256; //y = 96; y = 88; s_ingame.team.generic.type = MTYPE_PTEXT; s_ingame.team.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.team.generic.x = 320; s_ingame.team.generic.y = y; s_ingame.team.generic.id = ID_TEAM; s_ingame.team.generic.callback = InGame_Event; s_ingame.team.string = "START"; s_ingame.team.color = color_red; s_ingame.team.style = UI_CENTER|UI_SMALLFONT; y += INGAME_MENU_VERTICAL_SPACING; s_ingame.addbots.generic.type = MTYPE_PTEXT; s_ingame.addbots.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.addbots.generic.x = 320; s_ingame.addbots.generic.y = y; s_ingame.addbots.generic.id = ID_ADDBOTS; s_ingame.addbots.generic.callback = InGame_Event; s_ingame.addbots.string = "ADD BOTS"; s_ingame.addbots.color = color_red; s_ingame.addbots.style = UI_CENTER|UI_SMALLFONT; if( !trap_Cvar_VariableValue( "sv_running" ) || !trap_Cvar_VariableValue( "bot_enable" ) || (trap_Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER)) { s_ingame.addbots.generic.flags |= QMF_GRAYED; } y += INGAME_MENU_VERTICAL_SPACING; s_ingame.removebots.generic.type = MTYPE_PTEXT; s_ingame.removebots.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.removebots.generic.x = 320; s_ingame.removebots.generic.y = y; s_ingame.removebots.generic.id = ID_REMOVEBOTS; s_ingame.removebots.generic.callback = InGame_Event; s_ingame.removebots.string = "REMOVE BOTS"; s_ingame.removebots.color = color_red; s_ingame.removebots.style = UI_CENTER|UI_SMALLFONT; if( !trap_Cvar_VariableValue( "sv_running" ) || !trap_Cvar_VariableValue( "bot_enable" ) || (trap_Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER)) { s_ingame.removebots.generic.flags |= QMF_GRAYED; } y += INGAME_MENU_VERTICAL_SPACING; s_ingame.teamorders.generic.type = MTYPE_PTEXT; s_ingame.teamorders.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.teamorders.generic.x = 320; s_ingame.teamorders.generic.y = y; s_ingame.teamorders.generic.id = ID_TEAMORDERS; s_ingame.teamorders.generic.callback = InGame_Event; s_ingame.teamorders.string = "TEAM ORDERS"; s_ingame.teamorders.color = color_red; s_ingame.teamorders.style = UI_CENTER|UI_SMALLFONT; if( !(trap_Cvar_VariableValue( "g_gametype" ) >= GT_TEAM) || (trap_Cvar_VariableValue( "g_gametype" ) == GT_LMS ) ) { s_ingame.teamorders.generic.flags |= QMF_GRAYED; } else { trap_GetClientState( &cs ); trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING ); team = atoi( Info_ValueForKey( info, "t" ) ); if( team == TEAM_SPECTATOR ) { s_ingame.teamorders.generic.flags |= QMF_GRAYED; } } y += INGAME_MENU_VERTICAL_SPACING; s_ingame.vote.generic.type = MTYPE_PTEXT; s_ingame.vote.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.vote.generic.x = 320; s_ingame.vote.generic.y = y; s_ingame.vote.generic.id = ID_VOTE; s_ingame.vote.generic.callback = InGame_Event; s_ingame.vote.string = "CALL VOTE"; s_ingame.vote.color = color_red; s_ingame.vote.style = UI_CENTER|UI_SMALLFONT; trap_GetConfigString( CS_SERVERINFO, info, MAX_INFO_STRING ); if( atoi( Info_ValueForKey(info,"g_allowVote") )==0 || trap_Cvar_VariableValue("g_gametype")==GT_SINGLE_PLAYER ) { s_ingame.vote.generic.flags |= QMF_GRAYED; } y += INGAME_MENU_VERTICAL_SPACING; s_ingame.setup.generic.type = MTYPE_PTEXT; s_ingame.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.setup.generic.x = 320; s_ingame.setup.generic.y = y; s_ingame.setup.generic.id = ID_SETUP; s_ingame.setup.generic.callback = InGame_Event; s_ingame.setup.string = "SETUP"; s_ingame.setup.color = color_red; s_ingame.setup.style = UI_CENTER|UI_SMALLFONT; y += INGAME_MENU_VERTICAL_SPACING; s_ingame.server.generic.type = MTYPE_PTEXT; s_ingame.server.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.server.generic.x = 320; s_ingame.server.generic.y = y; s_ingame.server.generic.id = ID_SERVERINFO; s_ingame.server.generic.callback = InGame_Event; s_ingame.server.string = "SERVER INFO"; s_ingame.server.color = color_red; s_ingame.server.style = UI_CENTER|UI_SMALLFONT; y += INGAME_MENU_VERTICAL_SPACING; s_ingame.restart.generic.type = MTYPE_PTEXT; s_ingame.restart.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.restart.generic.x = 320; s_ingame.restart.generic.y = y; s_ingame.restart.generic.id = ID_RESTART; s_ingame.restart.generic.callback = InGame_Event; s_ingame.restart.string = "RESTART ARENA"; s_ingame.restart.color = color_red; s_ingame.restart.style = UI_CENTER|UI_SMALLFONT; if( !trap_Cvar_VariableValue( "sv_running" ) ) { s_ingame.restart.generic.flags |= QMF_GRAYED; } y += INGAME_MENU_VERTICAL_SPACING; s_ingame.resume.generic.type = MTYPE_PTEXT; s_ingame.resume.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.resume.generic.x = 320; s_ingame.resume.generic.y = y; s_ingame.resume.generic.id = ID_RESUME; s_ingame.resume.generic.callback = InGame_Event; s_ingame.resume.string = "RESUME GAME"; s_ingame.resume.color = color_red; s_ingame.resume.style = UI_CENTER|UI_SMALLFONT; y += INGAME_MENU_VERTICAL_SPACING; s_ingame.leave.generic.type = MTYPE_PTEXT; s_ingame.leave.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.leave.generic.x = 320; s_ingame.leave.generic.y = y; s_ingame.leave.generic.id = ID_LEAVEARENA; s_ingame.leave.generic.callback = InGame_Event; s_ingame.leave.string = "LEAVE ARENA"; s_ingame.leave.color = color_red; s_ingame.leave.style = UI_CENTER|UI_SMALLFONT; y += INGAME_MENU_VERTICAL_SPACING; s_ingame.quit.generic.type = MTYPE_PTEXT; s_ingame.quit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_ingame.quit.generic.x = 320; s_ingame.quit.generic.y = y; s_ingame.quit.generic.id = ID_QUIT; s_ingame.quit.generic.callback = InGame_Event; s_ingame.quit.string = "EXIT GAME"; s_ingame.quit.color = color_red; s_ingame.quit.style = UI_CENTER|UI_SMALLFONT; Menu_AddItem( &s_ingame.menu, &s_ingame.frame ); Menu_AddItem( &s_ingame.menu, &s_ingame.team ); Menu_AddItem( &s_ingame.menu, &s_ingame.addbots ); Menu_AddItem( &s_ingame.menu, &s_ingame.removebots ); Menu_AddItem( &s_ingame.menu, &s_ingame.teamorders ); Menu_AddItem( &s_ingame.menu, &s_ingame.vote ); Menu_AddItem( &s_ingame.menu, &s_ingame.setup ); Menu_AddItem( &s_ingame.menu, &s_ingame.server ); Menu_AddItem( &s_ingame.menu, &s_ingame.restart ); Menu_AddItem( &s_ingame.menu, &s_ingame.resume ); Menu_AddItem( &s_ingame.menu, &s_ingame.leave ); Menu_AddItem( &s_ingame.menu, &s_ingame.quit ); } /* ================= InGame_Cache ================= */ void InGame_Cache( void ) { trap_R_RegisterShaderNoMip( INGAME_FRAME ); } /* ================= UI_InGameMenu ================= */ void UI_InGameMenu( void ) { // force as top level menu uis.menusp = 0; // set menu cursor to a nice location uis.cursorx = 319; uis.cursory = 80; InGame_MenuInit(); UI_PushMenu( &s_ingame.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_serverinfo.c0000644000175000017500000001534211656310261020205 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" #define SERVERINFO_FRAMEL "menu/art_blueish/frame2_l" #define SERVERINFO_FRAMER "menu/art_blueish/frame1_r" #define SERVERINFO_BACK0 "menu/art_blueish/back_0" #define SERVERINFO_BACK1 "menu/art_blueish/back_1" static char* serverinfo_artlist[] = { SERVERINFO_FRAMEL, SERVERINFO_FRAMER, SERVERINFO_BACK0, SERVERINFO_BACK1, NULL }; #define ID_ADD 100 #define ID_BACK 101 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menubitmap_s back; menutext_s add; char info[MAX_INFO_STRING]; int numlines; } serverinfo_t; static serverinfo_t s_serverinfo; /* ================= Favorites_Add Add current server to favorites ================= */ void Favorites_Add( void ) { char adrstr[128]; char serverbuff[128]; int i; int best; trap_Cvar_VariableStringBuffer( "cl_currentServerAddress", serverbuff, sizeof(serverbuff) ); if (!serverbuff[0]) return; best = 0; for (i=0; i '9' ) && !best) best = i+1; } if (best) trap_Cvar_Set( va("server%d",best), serverbuff); } /* ================= ServerInfo_Event ================= */ static void ServerInfo_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_ADD: if (event != QM_ACTIVATED) break; Favorites_Add(); UI_PopMenu(); break; case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; } } /* ================= ServerInfo_MenuDraw ================= */ static void ServerInfo_MenuDraw( void ) { const char *s; char key[MAX_INFO_KEY]; char value[MAX_INFO_VALUE]; int i=0,y; y = SCREEN_HEIGHT/2 - s_serverinfo.numlines*(SMALLCHAR_HEIGHT)/2 - 20; s = s_serverinfo.info; while ( s && i < s_serverinfo.numlines ) { Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } Q_strcat( key, MAX_INFO_KEY, ":" ); UI_DrawString(SCREEN_WIDTH*0.50 - 8,y,key,UI_RIGHT|UI_SMALLFONT,color_red); UI_DrawString(SCREEN_WIDTH*0.50 + 8,y,value,UI_LEFT|UI_SMALLFONT,text_color_normal); y += SMALLCHAR_HEIGHT; i++; } Menu_Draw( &s_serverinfo.menu ); } /* ================= ServerInfo_MenuKey ================= */ static sfxHandle_t ServerInfo_MenuKey( int key ) { return ( Menu_DefaultKey( &s_serverinfo.menu, key ) ); } /* ================= ServerInfo_Cache ================= */ void ServerInfo_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!serverinfo_artlist[i]) break; trap_R_RegisterShaderNoMip(serverinfo_artlist[i]); } } /* ================= UI_ServerInfoMenu ================= */ void UI_ServerInfoMenu( void ) { const char *s; char key[MAX_INFO_KEY]; char value[MAX_INFO_VALUE]; // zero set all our globals memset( &s_serverinfo, 0 ,sizeof(serverinfo_t) ); ServerInfo_Cache(); s_serverinfo.menu.draw = ServerInfo_MenuDraw; s_serverinfo.menu.key = ServerInfo_MenuKey; s_serverinfo.menu.wrapAround = qtrue; s_serverinfo.menu.fullscreen = qtrue; s_serverinfo.banner.generic.type = MTYPE_BTEXT; s_serverinfo.banner.generic.x = 320; s_serverinfo.banner.generic.y = 16; s_serverinfo.banner.string = "SERVER INFO"; s_serverinfo.banner.color = color_white; s_serverinfo.banner.style = UI_CENTER; s_serverinfo.framel.generic.type = MTYPE_BITMAP; s_serverinfo.framel.generic.name = SERVERINFO_FRAMEL; s_serverinfo.framel.generic.flags = QMF_INACTIVE; s_serverinfo.framel.generic.x = 0; s_serverinfo.framel.generic.y = 78; s_serverinfo.framel.width = 256; s_serverinfo.framel.height = 329; s_serverinfo.framer.generic.type = MTYPE_BITMAP; s_serverinfo.framer.generic.name = SERVERINFO_FRAMER; s_serverinfo.framer.generic.flags = QMF_INACTIVE; s_serverinfo.framer.generic.x = 376; s_serverinfo.framer.generic.y = 76; s_serverinfo.framer.width = 256; s_serverinfo.framer.height = 334; s_serverinfo.add.generic.type = MTYPE_PTEXT; s_serverinfo.add.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_serverinfo.add.generic.callback = ServerInfo_Event; s_serverinfo.add.generic.id = ID_ADD; s_serverinfo.add.generic.x = 320; s_serverinfo.add.generic.y = 371; s_serverinfo.add.string = "ADD TO FAVORITES"; s_serverinfo.add.style = UI_CENTER|UI_SMALLFONT; s_serverinfo.add.color = color_red; if( trap_Cvar_VariableValue( "sv_running" ) ) { s_serverinfo.add.generic.flags |= QMF_GRAYED; } s_serverinfo.back.generic.type = MTYPE_BITMAP; s_serverinfo.back.generic.name = SERVERINFO_BACK0; s_serverinfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_serverinfo.back.generic.callback = ServerInfo_Event; s_serverinfo.back.generic.id = ID_BACK; s_serverinfo.back.generic.x = 0; s_serverinfo.back.generic.y = 480-64; s_serverinfo.back.width = 128; s_serverinfo.back.height = 64; s_serverinfo.back.focuspic = SERVERINFO_BACK1; trap_GetConfigString( CS_SERVERINFO, s_serverinfo.info, MAX_INFO_STRING ); s_serverinfo.numlines = 0; s = s_serverinfo.info; while ( s ) { Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } s_serverinfo.numlines++; } if (s_serverinfo.numlines > 16) s_serverinfo.numlines = 16; Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.banner ); Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.framel ); Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.framer ); Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.add ); Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.back ); UI_PushMenu( &s_serverinfo.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_removebots.c0000644000175000017500000002430511656310261020207 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= REMOVE BOTS MENU ======================================================================= */ #include "ui_local.h" #define ART_BACKGROUND "menu/art_blueish/addbotframe" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_DELETE0 "menu/art_blueish/delete_0" #define ART_DELETE1 "menu/art_blueish/delete_1" #define ART_ARROWS "menu/art_blueish/arrows_vert_0" #define ART_ARROWUP "menu/art_blueish/arrows_vert_top" #define ART_ARROWDOWN "menu/art_blueish/arrows_vert_bot" #define ID_UP 10 #define ID_DOWN 11 #define ID_DELETE 12 #define ID_BACK 13 #define ID_BOTNAME0 20 #define ID_BOTNAME1 21 #define ID_BOTNAME2 22 #define ID_BOTNAME3 23 #define ID_BOTNAME4 24 #define ID_BOTNAME5 25 #define ID_BOTNAME6 26 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s background; menubitmap_s arrows; menubitmap_s up; menubitmap_s down; menutext_s bots[7]; menubitmap_s delete; menubitmap_s back; int numBots; int baseBotNum; int selectedBotNum; char botnames[7][32]; int botClientNums[MAX_BOTS]; } removeBotsMenuInfo_t; static removeBotsMenuInfo_t removeBotsMenuInfo; /* ================= UI_RemoveBotsMenu_SetBotNames ================= */ static void UI_RemoveBotsMenu_SetBotNames( void ) { int n; char info[MAX_INFO_STRING]; for ( n = 0; (n < 7) && (removeBotsMenuInfo.baseBotNum + n < removeBotsMenuInfo.numBots); n++ ) { trap_GetConfigString( CS_PLAYERS + removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.baseBotNum + n], info, MAX_INFO_STRING ); Q_strncpyz( removeBotsMenuInfo.botnames[n], Info_ValueForKey( info, "n" ), sizeof(removeBotsMenuInfo.botnames[n]) ); Q_CleanStr( removeBotsMenuInfo.botnames[n] ); } } /* ================= UI_RemoveBotsMenu_DeleteEvent ================= */ static void UI_RemoveBotsMenu_DeleteEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } trap_Cmd_ExecuteText( EXEC_APPEND, va("clientkick %i\n", removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.baseBotNum + removeBotsMenuInfo.selectedBotNum]) ); } /* ================= UI_RemoveBotsMenu_BotEvent ================= */ static void UI_RemoveBotsMenu_BotEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } removeBotsMenuInfo.bots[removeBotsMenuInfo.selectedBotNum].color = color_orange; removeBotsMenuInfo.selectedBotNum = ((menucommon_s*)ptr)->id - ID_BOTNAME0; removeBotsMenuInfo.bots[removeBotsMenuInfo.selectedBotNum].color = color_white; } /* ================= UI_RemoveAddBotsMenu_BackEvent ================= */ static void UI_RemoveBotsMenu_BackEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } UI_PopMenu(); } /* ================= UI_RemoveBotsMenu_UpEvent ================= */ static void UI_RemoveBotsMenu_UpEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( removeBotsMenuInfo.baseBotNum > 0 ) { removeBotsMenuInfo.baseBotNum--; UI_RemoveBotsMenu_SetBotNames(); } } /* ================= UI_RemoveBotsMenu_DownEvent ================= */ static void UI_RemoveBotsMenu_DownEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } if( removeBotsMenuInfo.baseBotNum + 7 < removeBotsMenuInfo.numBots ) { removeBotsMenuInfo.baseBotNum++; UI_RemoveBotsMenu_SetBotNames(); } } /* ================= UI_RemoveBotsMenu_GetBots ================= */ static void UI_RemoveBotsMenu_GetBots( void ) { int numPlayers; int isBot; int n; char info[MAX_INFO_STRING]; trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); numPlayers = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); removeBotsMenuInfo.numBots = 0; for( n = 0; n < numPlayers; n++ ) { trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); isBot = atoi( Info_ValueForKey( info, "skill" ) ); if( !isBot ) { continue; } removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.numBots] = n; removeBotsMenuInfo.numBots++; } } /* ================= UI_RemoveBots_Cache ================= */ void UI_RemoveBots_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACKGROUND ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_DELETE0 ); trap_R_RegisterShaderNoMip( ART_DELETE1 ); } /* ================= UI_RemoveBotsMenu_Init ================= */ static void UI_RemoveBotsMenu_Init( void ) { int n; int count; int y; memset( &removeBotsMenuInfo, 0 ,sizeof(removeBotsMenuInfo) ); removeBotsMenuInfo.menu.fullscreen = qfalse; removeBotsMenuInfo.menu.wrapAround = qtrue; UI_RemoveBots_Cache(); UI_RemoveBotsMenu_GetBots(); UI_RemoveBotsMenu_SetBotNames(); count = removeBotsMenuInfo.numBots < 7 ? removeBotsMenuInfo.numBots : 7; removeBotsMenuInfo.banner.generic.type = MTYPE_BTEXT; removeBotsMenuInfo.banner.generic.x = 320; removeBotsMenuInfo.banner.generic.y = 16; removeBotsMenuInfo.banner.string = "REMOVE BOTS"; removeBotsMenuInfo.banner.color = color_white; removeBotsMenuInfo.banner.style = UI_CENTER; removeBotsMenuInfo.background.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.background.generic.name = ART_BACKGROUND; removeBotsMenuInfo.background.generic.flags = QMF_INACTIVE; removeBotsMenuInfo.background.generic.x = 320-233; removeBotsMenuInfo.background.generic.y = 240-166; removeBotsMenuInfo.background.width = 466; removeBotsMenuInfo.background.height = 332; removeBotsMenuInfo.arrows.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.arrows.generic.name = ART_ARROWS; removeBotsMenuInfo.arrows.generic.flags = QMF_INACTIVE; removeBotsMenuInfo.arrows.generic.x = 200; removeBotsMenuInfo.arrows.generic.y = 128; removeBotsMenuInfo.arrows.width = 64; removeBotsMenuInfo.arrows.height = 128; removeBotsMenuInfo.up.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; removeBotsMenuInfo.up.generic.x = 200; removeBotsMenuInfo.up.generic.y = 128; removeBotsMenuInfo.up.generic.id = ID_UP; removeBotsMenuInfo.up.generic.callback = UI_RemoveBotsMenu_UpEvent; removeBotsMenuInfo.up.width = 64; removeBotsMenuInfo.up.height = 64; removeBotsMenuInfo.up.focuspic = ART_ARROWUP; removeBotsMenuInfo.down.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; removeBotsMenuInfo.down.generic.x = 200; removeBotsMenuInfo.down.generic.y = 128+64; removeBotsMenuInfo.down.generic.id = ID_DOWN; removeBotsMenuInfo.down.generic.callback = UI_RemoveBotsMenu_DownEvent; removeBotsMenuInfo.down.width = 64; removeBotsMenuInfo.down.height = 64; removeBotsMenuInfo.down.focuspic = ART_ARROWDOWN; for( n = 0, y = 120; n < count; n++, y += 20 ) { removeBotsMenuInfo.bots[n].generic.type = MTYPE_PTEXT; removeBotsMenuInfo.bots[n].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; removeBotsMenuInfo.bots[n].generic.id = ID_BOTNAME0 + n; removeBotsMenuInfo.bots[n].generic.x = 320 - 56; removeBotsMenuInfo.bots[n].generic.y = y; removeBotsMenuInfo.bots[n].generic.callback = UI_RemoveBotsMenu_BotEvent; removeBotsMenuInfo.bots[n].string = removeBotsMenuInfo.botnames[n]; removeBotsMenuInfo.bots[n].color = color_orange; removeBotsMenuInfo.bots[n].style = UI_LEFT|UI_SMALLFONT; } removeBotsMenuInfo.delete.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.delete.generic.name = ART_DELETE0; removeBotsMenuInfo.delete.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; removeBotsMenuInfo.delete.generic.id = ID_DELETE; removeBotsMenuInfo.delete.generic.callback = UI_RemoveBotsMenu_DeleteEvent; removeBotsMenuInfo.delete.generic.x = 320+128-128; removeBotsMenuInfo.delete.generic.y = 256+128-64; removeBotsMenuInfo.delete.width = 128; removeBotsMenuInfo.delete.height = 64; removeBotsMenuInfo.delete.focuspic = ART_DELETE1; removeBotsMenuInfo.back.generic.type = MTYPE_BITMAP; removeBotsMenuInfo.back.generic.name = ART_BACK0; removeBotsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; removeBotsMenuInfo.back.generic.id = ID_BACK; removeBotsMenuInfo.back.generic.callback = UI_RemoveBotsMenu_BackEvent; removeBotsMenuInfo.back.generic.x = 320-128; removeBotsMenuInfo.back.generic.y = 256+128-64; removeBotsMenuInfo.back.width = 128; removeBotsMenuInfo.back.height = 64; removeBotsMenuInfo.back.focuspic = ART_BACK1; Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.background ); Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.banner ); Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.arrows ); Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.up ); Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.down ); for( n = 0; n < count; n++ ) { Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.bots[n] ); } Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.delete ); Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.back ); removeBotsMenuInfo.baseBotNum = 0; removeBotsMenuInfo.selectedBotNum = 0; removeBotsMenuInfo.bots[0].color = color_white; } /* ================= UI_RemoveBotsMenu ================= */ void UI_RemoveBotsMenu( void ) { UI_RemoveBotsMenu_Init(); UI_PushMenu( &removeBotsMenuInfo.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_gametype.c0000644000175000017500000003505211656310261021560 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define VOTEMENU_BACK0 "menu/art_blueish/back_0" #define VOTEMENU_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" static char* votemenu_Gametype_artlist[] = { VOTEMENU_BACK0, VOTEMENU_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; #define ID_BACK 100 #define ID_GO 101 #define ID_FFA 102 #define ID_Tourney 103 #define ID_TDM 104 #define ID_CTF 105 #define ID_1FCTF 106 #define ID_Overload 107 #define ID_Harvester 108 #define ID_Elimination 109 #define ID_CTFe 110 #define ID_LMS 111 #define ID_DOUBLED 112 #define ID_DOM 113 #define Gametype_MENU_VERTICAL_SPACING 19 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s go; //Buttons: menutext_s bFFA; menutext_s bTourney; menutext_s bTDM; menutext_s bCTF; menutext_s b1FCTF; menutext_s bOverload; menutext_s bHarvester; menutext_s bElimination; menutext_s bCTFe; menutext_s bLMS; menutext_s bDOUBLED; menutext_s bDOM; //Allowed: qboolean FFA; qboolean Tourney; qboolean TDM; qboolean CTF; qboolean _1FCTF; qboolean Overload; qboolean Harvester; qboolean Elimination; qboolean CTFe; qboolean LMS; qboolean DOUBLED; qboolean DOM; int selection; } votemenu_t; static votemenu_t s_votemenu_Gametype; void UI_VoteGametypeMenuInternal( void ); /* ================= VoteMenu_Gametype_Event ================= */ static void VoteMenu_Gametype_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } switch(s_votemenu_Gametype.selection) { case ID_FFA: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 0" ); UI_PopMenu(); UI_PopMenu(); break; case ID_Tourney: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 1" ); UI_PopMenu(); UI_PopMenu(); break; case ID_TDM: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 3" ); UI_PopMenu(); UI_PopMenu(); break; case ID_CTF: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 4" ); UI_PopMenu(); UI_PopMenu(); break; case ID_1FCTF: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 5" ); UI_PopMenu(); UI_PopMenu(); break; case ID_Overload: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 6" ); UI_PopMenu(); UI_PopMenu(); break; case ID_Harvester: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 7" ); UI_PopMenu(); UI_PopMenu(); break; case ID_Elimination: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 8" ); UI_PopMenu(); UI_PopMenu(); break; case ID_CTFe: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 9" ); UI_PopMenu(); UI_PopMenu(); break; case ID_LMS: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 10" ); UI_PopMenu(); UI_PopMenu(); break; case ID_DOUBLED: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 11" ); UI_PopMenu(); UI_PopMenu(); break; case ID_DOM: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote g_gametype 12" ); UI_PopMenu(); UI_PopMenu(); break; }; break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_Gametype.selection != ((menucommon_s*)ptr)->id) { s_votemenu_Gametype.selection = ((menucommon_s*)ptr)->id; UI_VoteGametypeMenuInternal(); } break; } } static void setGametypeMenutext(menutext_s *menu,int y,int id,qboolean active,char *text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if(!active) menu->generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu_Gametype.selection == id) menu->color = color_orange; menu->generic.x = 320; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteMenu_Gametype_Event; menu->string = text; menu->style = UI_CENTER|UI_SMALLFONT; } /* ================= VoteMenu_Gametype_MenuKey ================= */ /*static sfxHandle_t VoteMenu_Gametype_MenuKey( int key ) { return ( Menu_DefaultKey( &s_votemenu_Gametype.menu, key ) ); }*/ /* ================= VoteMenu_Gametype_Cache ================= */ static void VoteMenu_Gametype_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!votemenu_Gametype_artlist[i]) break; trap_R_RegisterShaderNoMip(votemenu_Gametype_artlist[i]); } } /* ================= UI_VoteMenu_Gametype_Draw ================= */ static void UI_VoteMenu_Gametype_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE GAMETYPE", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_Gametype.menu ); } /* ================= UI_VoteGametypeMenuInternal *Used then forcing a redraw ================= */ void UI_VoteGametypeMenuInternal( void ) { int y; VoteMenu_Gametype_Cache(); s_votemenu_Gametype.menu.wrapAround = qtrue; s_votemenu_Gametype.menu.fullscreen = qfalse; s_votemenu_Gametype.menu.draw = UI_VoteMenu_Gametype_Draw; s_votemenu_Gametype.banner.generic.type = MTYPE_BTEXT; s_votemenu_Gametype.banner.generic.x = 320; s_votemenu_Gametype.banner.generic.y = 16; s_votemenu_Gametype.banner.string = "CALL VOTE GAMETYPE"; s_votemenu_Gametype.banner.color = color_white; s_votemenu_Gametype.banner.style = UI_CENTER; y = 98; setGametypeMenutext(&s_votemenu_Gametype.bFFA,y,ID_FFA,s_votemenu_Gametype.FFA,"Free for all"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bTourney,y,ID_Tourney,s_votemenu_Gametype.Tourney,"Tournament"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bTDM,y,ID_TDM,s_votemenu_Gametype.TDM,"Team Deathmatch"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bCTF,y,ID_CTF,s_votemenu_Gametype.CTF,"Capture the flag"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.b1FCTF,y,ID_1FCTF,s_votemenu_Gametype._1FCTF,"One flag capture"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bOverload,y,ID_Overload,s_votemenu_Gametype.Overload,"Overload"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bHarvester,y,ID_Harvester,s_votemenu_Gametype.Harvester,"Harvester"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bElimination,y,ID_Elimination,s_votemenu_Gametype.Elimination,"Elimination"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bCTFe,y,ID_CTFe,s_votemenu_Gametype.CTFe,"CTF Elimination"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bLMS,y,ID_LMS,s_votemenu_Gametype.LMS,"Last man standing"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bDOUBLED,y,ID_DOUBLED,s_votemenu_Gametype.DOUBLED,"Double Domination"); y+=Gametype_MENU_VERTICAL_SPACING; setGametypeMenutext(&s_votemenu_Gametype.bDOM,y,ID_DOM,s_votemenu_Gametype.DOM,"Domination"); s_votemenu_Gametype.back.generic.type = MTYPE_BITMAP; s_votemenu_Gametype.back.generic.name = VOTEMENU_BACK0; s_votemenu_Gametype.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_Gametype.back.generic.callback = VoteMenu_Gametype_Event; s_votemenu_Gametype.back.generic.id = ID_BACK; s_votemenu_Gametype.back.generic.x = 320-128; s_votemenu_Gametype.back.generic.y = 256+128-64; s_votemenu_Gametype.back.width = 128; s_votemenu_Gametype.back.height = 64; s_votemenu_Gametype.back.focuspic = VOTEMENU_BACK1; s_votemenu_Gametype.go.generic.type = MTYPE_BITMAP; s_votemenu_Gametype.go.generic.name = ART_FIGHT0; s_votemenu_Gametype.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_Gametype.go.generic.callback = VoteMenu_Gametype_Event; s_votemenu_Gametype.go.generic.id = ID_GO; s_votemenu_Gametype.go.generic.x = 320; s_votemenu_Gametype.go.generic.y = 256+128-64; s_votemenu_Gametype.go.width = 128; s_votemenu_Gametype.go.height = 64; s_votemenu_Gametype.go.focuspic = ART_FIGHT1; } /* ================= UI_VoteGametypeMenu *Called from outside ================= */ void UI_VoteGametypeMenu( void ) { char serverinfo[MAX_INFO_STRING], *gametypeinfo; // zero set all our globals memset( &s_votemenu_Gametype, 0 ,sizeof(votemenu_t) ); trap_GetConfigString( CS_SERVERINFO, serverinfo, MAX_INFO_STRING ); gametypeinfo = Info_ValueForKey(serverinfo,"g_voteGametypes"); if(!Q_stricmp(gametypeinfo,"*")) { s_votemenu_Gametype.FFA = qtrue; s_votemenu_Gametype.Tourney = qtrue; s_votemenu_Gametype.TDM = qtrue; s_votemenu_Gametype.CTF = qtrue; s_votemenu_Gametype._1FCTF = qtrue; s_votemenu_Gametype.Overload = qtrue; s_votemenu_Gametype.Harvester = qtrue; s_votemenu_Gametype.Elimination = qtrue; s_votemenu_Gametype.CTFe = qtrue; s_votemenu_Gametype.LMS = qtrue; s_votemenu_Gametype.DOUBLED = qtrue; s_votemenu_Gametype.DOM = qtrue; } else { s_votemenu_Gametype.FFA = (qboolean)Q_stristr(gametypeinfo,"/0/"); s_votemenu_Gametype.Tourney = (qboolean)Q_stristr(gametypeinfo,"/1/"); s_votemenu_Gametype.TDM = (qboolean)Q_stristr(gametypeinfo,"/3/"); s_votemenu_Gametype.CTF = (qboolean)Q_stristr(gametypeinfo,"/4/"); s_votemenu_Gametype._1FCTF = (qboolean)Q_stristr(gametypeinfo,"/5/"); s_votemenu_Gametype.Overload = (qboolean)Q_stristr(gametypeinfo,"/6/"); s_votemenu_Gametype.Harvester = (qboolean)Q_stristr(gametypeinfo,"/7/"); s_votemenu_Gametype.Elimination = (qboolean)Q_stristr(gametypeinfo,"/8/"); s_votemenu_Gametype.CTFe = (qboolean)Q_stristr(gametypeinfo,"/9/"); s_votemenu_Gametype.LMS = (qboolean)Q_stristr(gametypeinfo,"/10/"); s_votemenu_Gametype.DOUBLED = (qboolean)Q_stristr(gametypeinfo,"/11/"); s_votemenu_Gametype.DOM = (qboolean)Q_stristr(gametypeinfo,"/12/"); } UI_VoteGametypeMenuInternal(); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.banner ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.back ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.go ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bFFA ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bTourney ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bTDM ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bCTF ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.b1FCTF ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bOverload ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bHarvester ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bElimination ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bCTFe ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bLMS ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bDOUBLED ); Menu_AddItem( &s_votemenu_Gametype.menu, (void*) &s_votemenu_Gametype.bDOM ); UI_PushMenu( &s_votemenu_Gametype.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_sparena.c0000644000175000017500000000317211656310261017452 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" void UI_SPArena_Start( const char *arenaInfo ) { char *map; int level; int n; char *txt; n = (int)trap_Cvar_VariableValue( "sv_maxclients" ); if ( n < 8 ) { trap_Cvar_SetValue( "sv_maxclients", 8 ); } level = atoi( Info_ValueForKey( arenaInfo, "num" ) ); txt = Info_ValueForKey( arenaInfo, "special" ); if( txt[0] ) { if( Q_stricmp( txt, "training" ) == 0 ) { level = -4; } else if( Q_stricmp( txt, "final" ) == 0 ) { level = UI_GetNumSPTiers() * ARENAS_PER_TIER; } } trap_Cvar_SetValue( "ui_spSelection", level ); map = Info_ValueForKey( arenaInfo, "map" ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "spmap %s\n", map ) ); } openarena_0.8.8.orig/code/q3_ui/ui_network.c0000644000175000017500000002153211656310261017512 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= NETWORK OPTIONS MENU ======================================================================= */ #include "ui_local.h" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ID_GRAPHICS 10 #define ID_DISPLAY 11 #define ID_SOUND 12 #define ID_NETWORK 13 #define ID_RATE 14 #define ID_BACK 15 const char *rate_items[] = { "<= 28.8K", "33.6K", "56K", "ISDN", "LAN/Cable/xDSL", NULL }; typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s graphics; menutext_s display; menutext_s sound; menutext_s network; menulist_s rate; menubitmap_s back; } networkOptionsInfo_t; static networkOptionsInfo_t networkOptionsInfo; /* ================= UI_NetworkOptionsMenu_Event ================= */ static void UI_NetworkOptionsMenu_Event( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GRAPHICS: UI_PopMenu(); UI_GraphicsOptionsMenu(); break; case ID_DISPLAY: UI_PopMenu(); UI_DisplayOptionsMenu(); break; case ID_SOUND: UI_PopMenu(); UI_SoundOptionsMenu(); break; case ID_NETWORK: break; case ID_RATE: if( networkOptionsInfo.rate.curvalue == 0 ) { trap_Cvar_SetValue( "rate", 2500 ); } else if( networkOptionsInfo.rate.curvalue == 1 ) { trap_Cvar_SetValue( "rate", 3000 ); } else if( networkOptionsInfo.rate.curvalue == 2 ) { trap_Cvar_SetValue( "rate", 4000 ); } else if( networkOptionsInfo.rate.curvalue == 3 ) { trap_Cvar_SetValue( "rate", 5000 ); } else if( networkOptionsInfo.rate.curvalue == 4 ) { trap_Cvar_SetValue( "rate", 25000 ); } break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_NetworkOptionsMenu_Init =============== */ static void UI_NetworkOptionsMenu_Init( void ) { int y; int rate; memset( &networkOptionsInfo, 0, sizeof(networkOptionsInfo) ); UI_NetworkOptionsMenu_Cache(); networkOptionsInfo.menu.wrapAround = qtrue; networkOptionsInfo.menu.fullscreen = qtrue; networkOptionsInfo.banner.generic.type = MTYPE_BTEXT; networkOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY; networkOptionsInfo.banner.generic.x = 320; networkOptionsInfo.banner.generic.y = 16; networkOptionsInfo.banner.string = "SYSTEM SETUP"; networkOptionsInfo.banner.color = color_white; networkOptionsInfo.banner.style = UI_CENTER; networkOptionsInfo.framel.generic.type = MTYPE_BITMAP; networkOptionsInfo.framel.generic.name = ART_FRAMEL; networkOptionsInfo.framel.generic.flags = QMF_INACTIVE; networkOptionsInfo.framel.generic.x = 0; networkOptionsInfo.framel.generic.y = 78; networkOptionsInfo.framel.width = 256; networkOptionsInfo.framel.height = 329; networkOptionsInfo.framer.generic.type = MTYPE_BITMAP; networkOptionsInfo.framer.generic.name = ART_FRAMER; networkOptionsInfo.framer.generic.flags = QMF_INACTIVE; networkOptionsInfo.framer.generic.x = 376; networkOptionsInfo.framer.generic.y = 76; networkOptionsInfo.framer.width = 256; networkOptionsInfo.framer.height = 334; networkOptionsInfo.graphics.generic.type = MTYPE_PTEXT; networkOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; networkOptionsInfo.graphics.generic.id = ID_GRAPHICS; networkOptionsInfo.graphics.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.graphics.generic.x = 216; networkOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT; networkOptionsInfo.graphics.string = "GRAPHICS"; networkOptionsInfo.graphics.style = UI_RIGHT; networkOptionsInfo.graphics.color = color_red; networkOptionsInfo.display.generic.type = MTYPE_PTEXT; networkOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; networkOptionsInfo.display.generic.id = ID_DISPLAY; networkOptionsInfo.display.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.display.generic.x = 216; networkOptionsInfo.display.generic.y = 240 - PROP_HEIGHT; networkOptionsInfo.display.string = "DISPLAY"; networkOptionsInfo.display.style = UI_RIGHT; networkOptionsInfo.display.color = color_red; networkOptionsInfo.sound.generic.type = MTYPE_PTEXT; networkOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; networkOptionsInfo.sound.generic.id = ID_SOUND; networkOptionsInfo.sound.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.sound.generic.x = 216; networkOptionsInfo.sound.generic.y = 240; networkOptionsInfo.sound.string = "SOUND"; networkOptionsInfo.sound.style = UI_RIGHT; networkOptionsInfo.sound.color = color_red; networkOptionsInfo.network.generic.type = MTYPE_PTEXT; networkOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY; networkOptionsInfo.network.generic.id = ID_NETWORK; networkOptionsInfo.network.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.network.generic.x = 216; networkOptionsInfo.network.generic.y = 240 + PROP_HEIGHT; networkOptionsInfo.network.string = "NETWORK"; networkOptionsInfo.network.style = UI_RIGHT; networkOptionsInfo.network.color = color_red; y = 240 - 1 * (BIGCHAR_HEIGHT+2); networkOptionsInfo.rate.generic.type = MTYPE_SPINCONTROL; networkOptionsInfo.rate.generic.name = "Data Rate:"; networkOptionsInfo.rate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; networkOptionsInfo.rate.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.rate.generic.id = ID_RATE; networkOptionsInfo.rate.generic.x = 400; networkOptionsInfo.rate.generic.y = y; networkOptionsInfo.rate.itemnames = rate_items; networkOptionsInfo.back.generic.type = MTYPE_BITMAP; networkOptionsInfo.back.generic.name = ART_BACK0; networkOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; networkOptionsInfo.back.generic.callback = UI_NetworkOptionsMenu_Event; networkOptionsInfo.back.generic.id = ID_BACK; networkOptionsInfo.back.generic.x = 0; networkOptionsInfo.back.generic.y = 480-64; networkOptionsInfo.back.width = 128; networkOptionsInfo.back.height = 64; networkOptionsInfo.back.focuspic = ART_BACK1; Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.banner ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.framel ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.framer ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.graphics ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.display ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.sound ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.network ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.rate ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.back ); rate = trap_Cvar_VariableValue( "rate" ); if( rate <= 2500 ) { networkOptionsInfo.rate.curvalue = 0; } else if( rate <= 3000 ) { networkOptionsInfo.rate.curvalue = 1; } else if( rate <= 4000 ) { networkOptionsInfo.rate.curvalue = 2; } else if( rate <= 5000 ) { networkOptionsInfo.rate.curvalue = 3; } else { networkOptionsInfo.rate.curvalue = 4; } } /* =============== UI_NetworkOptionsMenu_Cache =============== */ void UI_NetworkOptionsMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); } /* =============== UI_NetworkOptionsMenu =============== */ void UI_NetworkOptionsMenu( void ) { UI_NetworkOptionsMenu_Init(); UI_PushMenu( &networkOptionsInfo.menu ); Menu_SetCursorToItem( &networkOptionsInfo.menu, &networkOptionsInfo.network ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_fraglimit.c0000644000175000017500000002354011656310261021722 0ustar smcvsmcv /* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define VOTEMENU_BACK0 "menu/art_blueish/back_0" #define VOTEMENU_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" static char* votemenu_fraglmit_artlist[] = { VOTEMENU_BACK0, VOTEMENU_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; #define ID_BACK 100 #define ID_GO 101 #define ID_50 102 #define ID_10 103 #define ID_15 104 #define ID_20 105 #define ID_30 106 #define ID_40 107 #define ID_INF 108 #define FRAGLIMIT_MENU_VERTICAL_SPACING 28 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s go; //Buttons: menutext_s bLimit50; menutext_s bLimit10; menutext_s bLimit15; menutext_s bLimit20; menutext_s bLimit30; menutext_s bLimit40; menutext_s bLimitInf; int min, max; //Values restricted by server int selection; } votemenu_t; static votemenu_t s_votemenu_fraglmit; void UI_VoteFraglimitMenuInternal( void ); /* ================= VoteMenu_Fraglimit_Event ================= */ static void VoteMenu_Fraglimit_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } switch(s_votemenu_fraglmit.selection) { case ID_10: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 10" ); UI_PopMenu(); UI_PopMenu(); break; case ID_15: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 15" ); UI_PopMenu(); UI_PopMenu(); break; case ID_20: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 20" ); UI_PopMenu(); UI_PopMenu(); break; case ID_30: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 30" ); UI_PopMenu(); UI_PopMenu(); break; case ID_40: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 40" ); UI_PopMenu(); UI_PopMenu(); break; case ID_50: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 50" ); UI_PopMenu(); UI_PopMenu(); break; case ID_INF: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote fraglimit 0" ); UI_PopMenu(); UI_PopMenu(); break; }; break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_fraglmit.selection != ((menucommon_s*)ptr)->id) { s_votemenu_fraglmit.selection = ((menucommon_s*)ptr)->id; UI_VoteFraglimitMenuInternal(); } break; } } static void setMenutext(menutext_s *menu,int y,int id,int value,char *text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if((s_votemenu_fraglmit.min > value && value!=0) || (s_votemenu_fraglmit.max < value && s_votemenu_fraglmit.max!=0) || (s_votemenu_fraglmit.max != 0 && value==0)) menu->generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu_fraglmit.selection == id) menu->color = color_orange; menu->generic.x = 320; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteMenu_Fraglimit_Event; menu->string = text; menu->style = UI_CENTER|UI_SMALLFONT; } /* ================= VoteMenu_Fraglimit_Cache ================= */ static void VoteMenu_Fraglimit_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!votemenu_fraglmit_artlist[i]) break; trap_R_RegisterShaderNoMip(votemenu_fraglmit_artlist[i]); } } /* ================= UI_VoteMenu_Fraglimit_Draw ================= */ static void UI_VoteMenu_Fraglimit_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE - FRAGLIMIT", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_fraglmit.menu ); } /* ================= UI_VoteFraglimitMenuInternal *Used then forcing a redraw ================= */ void UI_VoteFraglimitMenuInternal( void ) { int y; VoteMenu_Fraglimit_Cache(); s_votemenu_fraglmit.menu.wrapAround = qtrue; s_votemenu_fraglmit.menu.fullscreen = qfalse; s_votemenu_fraglmit.menu.draw = UI_VoteMenu_Fraglimit_Draw; s_votemenu_fraglmit.banner.generic.type = MTYPE_BTEXT; s_votemenu_fraglmit.banner.generic.x = 320; s_votemenu_fraglmit.banner.generic.y = 16; s_votemenu_fraglmit.banner.string = "CALL VOTE - FRAGLIMIT"; s_votemenu_fraglmit.banner.color = color_white; s_votemenu_fraglmit.banner.style = UI_CENTER; y = 98; setMenutext(&s_votemenu_fraglmit.bLimit10,y,ID_10,10,"10"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimit15,y,ID_15,15,"15"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimit20,y,ID_20,20,"20"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimit30,y,ID_30,30,"30"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimit40,y,ID_40,40,"40"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimit50,y,ID_50,50,"50"); y+=FRAGLIMIT_MENU_VERTICAL_SPACING; setMenutext(&s_votemenu_fraglmit.bLimitInf,y,ID_INF,0,"No limit"); s_votemenu_fraglmit.back.generic.type = MTYPE_BITMAP; s_votemenu_fraglmit.back.generic.name = VOTEMENU_BACK0; s_votemenu_fraglmit.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_fraglmit.back.generic.callback = VoteMenu_Fraglimit_Event; s_votemenu_fraglmit.back.generic.id = ID_BACK; s_votemenu_fraglmit.back.generic.x = 320-128; s_votemenu_fraglmit.back.generic.y = 256+128-64; s_votemenu_fraglmit.back.width = 128; s_votemenu_fraglmit.back.height = 64; s_votemenu_fraglmit.back.focuspic = VOTEMENU_BACK1; s_votemenu_fraglmit.go.generic.type = MTYPE_BITMAP; s_votemenu_fraglmit.go.generic.name = ART_FIGHT0; s_votemenu_fraglmit.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_fraglmit.go.generic.callback = VoteMenu_Fraglimit_Event; s_votemenu_fraglmit.go.generic.id = ID_GO; s_votemenu_fraglmit.go.generic.x = 320; s_votemenu_fraglmit.go.generic.y = 256+128-64; s_votemenu_fraglmit.go.width = 128; s_votemenu_fraglmit.go.height = 64; s_votemenu_fraglmit.go.focuspic = ART_FIGHT1; } /* ================= UI_VoteFraglimitMenu *Called from outside ================= */ void UI_VoteFraglimitMenu( void ) { char serverinfo[MAX_INFO_STRING]; // zero set all our globals memset( &s_votemenu_fraglmit, 0 ,sizeof(votemenu_t) ); trap_GetConfigString( CS_SERVERINFO, serverinfo, MAX_INFO_STRING ); s_votemenu_fraglmit.min = atoi(Info_ValueForKey(serverinfo,"g_voteMinFraglimit")); s_votemenu_fraglmit.max = atoi(Info_ValueForKey(serverinfo,"g_voteMaxFraglimit")); UI_VoteFraglimitMenuInternal(); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.banner ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.back ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.go ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit10 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit15 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit20 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit30 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit40 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimit50 ); Menu_AddItem( &s_votemenu_fraglmit.menu, (void*) &s_votemenu_fraglmit.bLimitInf ); UI_PushMenu( &s_votemenu_fraglmit.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_votemenu_timelimit.c0000644000175000017500000002365711656310261021752 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Poul Sander This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #define VOTEMENU_BACK0 "menu/art_blueish/back_0" #define VOTEMENU_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/accept_0" #define ART_FIGHT1 "menu/art_blueish/accept_1" #define ART_BACKGROUND "menu/art_blueish/addbotframe" static char* votemenu_Timelmit_artlist[] = { VOTEMENU_BACK0, VOTEMENU_BACK1, ART_FIGHT0, ART_FIGHT1, NULL }; #define ID_BACK 100 #define ID_GO 101 #define ID_50 102 #define ID_10 103 #define ID_15 104 #define ID_20 105 #define ID_30 106 #define ID_40 107 #define ID_INF 108 #define TimeLIMIT_MENU_VERTICAL_SPACING 28 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s back; menubitmap_s go; //Buttons: menutext_s bLimit50; menutext_s bLimit10; menutext_s bLimit15; menutext_s bLimit20; menutext_s bLimit30; menutext_s bLimit40; menutext_s bLimitInf; int min, max; //Values restricted by server int selection; } votemenu_t; static votemenu_t s_votemenu_Timelmit; void UI_VoteTimelimitMenuInternal( void ); /* ================= VoteMenu_Timelimit_Event ================= */ static void VoteMenu_Timelimit_Event( void* ptr, int event ) { switch (((menucommon_s*)ptr)->id) { case ID_BACK: if (event != QM_ACTIVATED) break; UI_PopMenu(); break; case ID_GO: if( event != QM_ACTIVATED ) { return; } switch(s_votemenu_Timelmit.selection) { case ID_10: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 10" ); UI_PopMenu(); UI_PopMenu(); break; case ID_15: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 15" ); UI_PopMenu(); UI_PopMenu(); break; case ID_20: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 20" ); UI_PopMenu(); UI_PopMenu(); break; case ID_30: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 30" ); UI_PopMenu(); UI_PopMenu(); break; case ID_40: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 40" ); UI_PopMenu(); UI_PopMenu(); break; case ID_50: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 50" ); UI_PopMenu(); UI_PopMenu(); break; case ID_INF: trap_Cmd_ExecuteText( EXEC_APPEND, "callvote timelimit 0" ); UI_PopMenu(); UI_PopMenu(); break; }; break; default: if( event != QM_ACTIVATED ) { return; } if(s_votemenu_Timelmit.selection != ((menucommon_s*)ptr)->id) { s_votemenu_Timelmit.selection = ((menucommon_s*)ptr)->id; UI_VoteTimelimitMenuInternal(); } break; } } static void setTimeMenutext(menutext_s *menu,int y,int id,int value,char *text) { menu->generic.type = MTYPE_PTEXT; menu->color = color_red; menu->generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; if((s_votemenu_Timelmit.min > value && value!=0) || (s_votemenu_Timelmit.max < value && s_votemenu_Timelmit.max!=0) || (s_votemenu_Timelmit.max != 0 && value==0)) menu->generic.flags |= QMF_INACTIVE|QMF_GRAYED; else if(s_votemenu_Timelmit.selection == id) menu->color = color_orange; menu->generic.x = 320; menu->generic.y = y; menu->generic.id = id; menu->generic.callback = VoteMenu_Timelimit_Event; menu->string = text; menu->style = UI_CENTER|UI_SMALLFONT; } /* ================= VoteMenu_Timelimit_Cache ================= */ static void VoteMenu_Timelimit_Cache( void ) { int i; // touch all our pics for (i=0; ;i++) { if (!votemenu_Timelmit_artlist[i]) break; trap_R_RegisterShaderNoMip(votemenu_Timelmit_artlist[i]); } } /* ================= UI_VoteMenu_Timelimit_Draw ================= */ static void UI_VoteMenu_Timelimit_Draw( void ) { UI_DrawBannerString( 320, 16, "CALL VOTE - TIMELIMIT", UI_CENTER, color_white ); UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND ); // standard menu drawing Menu_Draw( &s_votemenu_Timelmit.menu ); } /* ================= UI_VoteTimelimitMenuInternal *Used then forcing a redraw ================= */ void UI_VoteTimelimitMenuInternal( void ) { int y; VoteMenu_Timelimit_Cache(); s_votemenu_Timelmit.menu.wrapAround = qtrue; s_votemenu_Timelmit.menu.fullscreen = qfalse; s_votemenu_Timelmit.menu.draw = UI_VoteMenu_Timelimit_Draw; s_votemenu_Timelmit.banner.generic.type = MTYPE_BTEXT; s_votemenu_Timelmit.banner.generic.x = 320; s_votemenu_Timelmit.banner.generic.y = 16; s_votemenu_Timelmit.banner.string = "CALL VOTE - TIMELIMIT"; s_votemenu_Timelmit.banner.color = color_white; s_votemenu_Timelmit.banner.style = UI_CENTER; y = 98; setTimeMenutext(&s_votemenu_Timelmit.bLimit10,y,ID_10,10,"10 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimit15,y,ID_15,15,"15 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimit20,y,ID_20,20,"20 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimit30,y,ID_30,30,"30 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimit40,y,ID_40,40,"40 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimit50,y,ID_50,50,"50 minutes"); y+=TimeLIMIT_MENU_VERTICAL_SPACING; setTimeMenutext(&s_votemenu_Timelmit.bLimitInf,y,ID_INF,0,"No limit"); s_votemenu_Timelmit.back.generic.type = MTYPE_BITMAP; s_votemenu_Timelmit.back.generic.name = VOTEMENU_BACK0; s_votemenu_Timelmit.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_Timelmit.back.generic.callback = VoteMenu_Timelimit_Event; s_votemenu_Timelmit.back.generic.id = ID_BACK; s_votemenu_Timelmit.back.generic.x = 320-128; s_votemenu_Timelmit.back.generic.y = 256+128-64; s_votemenu_Timelmit.back.width = 128; s_votemenu_Timelmit.back.height = 64; s_votemenu_Timelmit.back.focuspic = VOTEMENU_BACK1; s_votemenu_Timelmit.go.generic.type = MTYPE_BITMAP; s_votemenu_Timelmit.go.generic.name = ART_FIGHT0; s_votemenu_Timelmit.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_votemenu_Timelmit.go.generic.callback = VoteMenu_Timelimit_Event; s_votemenu_Timelmit.go.generic.id = ID_GO; s_votemenu_Timelmit.go.generic.x = 320; s_votemenu_Timelmit.go.generic.y = 256+128-64; s_votemenu_Timelmit.go.width = 128; s_votemenu_Timelmit.go.height = 64; s_votemenu_Timelmit.go.focuspic = ART_FIGHT1; } /* ================= UI_VoteTimelimitMenu *Called from outside ================= */ void UI_VoteTimelimitMenu( void ) { char serverinfo[MAX_INFO_STRING]; // zero set all our globals memset( &s_votemenu_Timelmit, 0 ,sizeof(votemenu_t) ); trap_GetConfigString( CS_SERVERINFO, serverinfo, MAX_INFO_STRING ); s_votemenu_Timelmit.min = atoi(Info_ValueForKey(serverinfo,"g_voteMinTimelimit")); s_votemenu_Timelmit.max = atoi(Info_ValueForKey(serverinfo,"g_voteMaxTimelimit")); UI_VoteTimelimitMenuInternal(); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.banner ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.back ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.go ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit10 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit15 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit20 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit30 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit40 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimit50 ); Menu_AddItem( &s_votemenu_Timelmit.menu, (void*) &s_votemenu_Timelmit.bLimitInf ); UI_PushMenu( &s_votemenu_Timelmit.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_main.c0000644000175000017500000002326211656310261016747 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= USER INTERFACE MAIN ======================================================================= */ #include "ui_local.h" /* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .qvm file ================ */ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case UI_GETAPIVERSION: return UI_API_VERSION; case UI_INIT: UI_Init(); return 0; case UI_SHUTDOWN: UI_Shutdown(); return 0; case UI_KEY_EVENT: UI_KeyEvent( arg0, arg1 ); return 0; case UI_MOUSE_EVENT: UI_MouseEvent( arg0, arg1 ); return 0; case UI_REFRESH: UI_Refresh( arg0 ); return 0; case UI_IS_FULLSCREEN: return UI_IsFullscreen(); case UI_SET_ACTIVE_MENU: UI_SetActiveMenu( arg0 ); return 0; case UI_CONSOLE_COMMAND: return UI_ConsoleCommand(arg0); case UI_DRAW_CONNECT_SCREEN: UI_DrawConnectScreen( arg0 ); return 0; case UI_HASUNIQUECDKEY: // mod authors need to observe this return qtrue; // bk010117 - change this to qfalse for mods! } return -1; } /* ================ cvars ================ */ typedef struct { vmCvar_t *vmCvar; char *cvarName; char *defaultString; int cvarFlags; } cvarTable_t; vmCvar_t ui_ffa_fraglimit; vmCvar_t ui_ffa_timelimit; vmCvar_t ui_tourney_fraglimit; vmCvar_t ui_tourney_timelimit; vmCvar_t ui_team_fraglimit; vmCvar_t ui_team_timelimit; vmCvar_t ui_team_friendly; vmCvar_t ui_ctf_capturelimit; vmCvar_t ui_ctf_timelimit; vmCvar_t ui_ctf_friendly; vmCvar_t ui_1fctf_capturelimit; vmCvar_t ui_1fctf_timelimit; vmCvar_t ui_1fctf_friendly; vmCvar_t ui_overload_capturelimit; vmCvar_t ui_overload_timelimit; vmCvar_t ui_overload_friendly; vmCvar_t ui_harvester_capturelimit; vmCvar_t ui_harvester_timelimit; vmCvar_t ui_harvester_friendly; vmCvar_t ui_elimination_capturelimit; vmCvar_t ui_elimination_timelimit; vmCvar_t ui_ctf_elimination_capturelimit; vmCvar_t ui_ctf_elimination_timelimit; vmCvar_t ui_lms_fraglimit; vmCvar_t ui_lms_timelimit; vmCvar_t ui_dd_capturelimit; vmCvar_t ui_dd_timelimit; vmCvar_t ui_dd_friendly; vmCvar_t ui_dom_capturelimit; vmCvar_t ui_dom_timelimit; vmCvar_t ui_dom_friendly; vmCvar_t ui_arenasFile; vmCvar_t ui_botsFile; vmCvar_t ui_spScores1; vmCvar_t ui_spScores2; vmCvar_t ui_spScores3; vmCvar_t ui_spScores4; vmCvar_t ui_spScores5; vmCvar_t ui_spAwards; vmCvar_t ui_spVideos; vmCvar_t ui_spSkill; vmCvar_t ui_spSelection; vmCvar_t ui_browserMaster; vmCvar_t ui_browserGameType; vmCvar_t ui_browserSortKey; vmCvar_t ui_browserShowFull; vmCvar_t ui_browserShowEmpty; vmCvar_t ui_brassTime; vmCvar_t ui_drawCrosshair; vmCvar_t ui_drawCrosshairNames; vmCvar_t ui_marks; vmCvar_t ui_server1; vmCvar_t ui_server2; vmCvar_t ui_server3; vmCvar_t ui_server4; vmCvar_t ui_server5; vmCvar_t ui_server6; vmCvar_t ui_server7; vmCvar_t ui_server8; vmCvar_t ui_server9; vmCvar_t ui_server10; vmCvar_t ui_server11; vmCvar_t ui_server12; vmCvar_t ui_server13; vmCvar_t ui_server14; vmCvar_t ui_server15; vmCvar_t ui_server16; //vmCvar_t ui_cdkeychecked; //new in beta 23: vmCvar_t ui_browserOnlyHumans; //new in beta 37: vmCvar_t ui_setupchecked; // bk001129 - made static to avoid aliasing. static cvarTable_t cvarTable[] = { { &ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", CVAR_ARCHIVE }, { &ui_ffa_timelimit, "ui_ffa_timelimit", "0", CVAR_ARCHIVE }, { &ui_tourney_fraglimit, "ui_tourney_fraglimit", "0", CVAR_ARCHIVE }, { &ui_tourney_timelimit, "ui_tourney_timelimit", "15", CVAR_ARCHIVE }, { &ui_team_fraglimit, "ui_team_fraglimit", "0", CVAR_ARCHIVE }, { &ui_team_timelimit, "ui_team_timelimit", "20", CVAR_ARCHIVE }, { &ui_team_friendly, "ui_team_friendly", "1", CVAR_ARCHIVE }, { &ui_ctf_capturelimit, "ui_ctf_capturelimit", "8", CVAR_ARCHIVE }, { &ui_ctf_timelimit, "ui_ctf_timelimit", "30", CVAR_ARCHIVE }, { &ui_ctf_friendly, "ui_ctf_friendly", "0", CVAR_ARCHIVE }, { &ui_1fctf_capturelimit, "ui_1fctf_capturelimit", "8", CVAR_ARCHIVE }, { &ui_1fctf_timelimit, "ui_1fctf_timelimit", "30", CVAR_ARCHIVE }, { &ui_1fctf_friendly, "ui_1fctf_friendly", "0", CVAR_ARCHIVE }, { &ui_overload_capturelimit, "ui_overload_capturelimit", "8", CVAR_ARCHIVE }, { &ui_overload_timelimit, "ui_overload_timelimit", "30", CVAR_ARCHIVE }, { &ui_overload_friendly, "ui_overload_friendly", "0", CVAR_ARCHIVE }, { &ui_harvester_capturelimit, "ui_harvester_capturelimit", "20", CVAR_ARCHIVE }, { &ui_harvester_timelimit, "ui_harvester_timelimit", "30", CVAR_ARCHIVE }, { &ui_harvester_friendly, "ui_harvester_friendly", "0", CVAR_ARCHIVE }, { &ui_elimination_capturelimit, "ui_elimination_capturelimit", "8", CVAR_ARCHIVE }, { &ui_elimination_timelimit, "ui_elimination_timelimit", "20", CVAR_ARCHIVE }, { &ui_ctf_elimination_capturelimit, "ui_ctf_elimination_capturelimit", "8", CVAR_ARCHIVE }, { &ui_ctf_elimination_timelimit, "ui_ctf_elimination_timelimit", "30", CVAR_ARCHIVE }, { &ui_lms_fraglimit, "ui_lms_fraglimit", "20", CVAR_ARCHIVE }, { &ui_lms_timelimit, "ui_lms_timelimit", "0", CVAR_ARCHIVE }, { &ui_dd_capturelimit, "ui_dd_capturelimit", "8", CVAR_ARCHIVE }, { &ui_dd_timelimit, "ui_dd_timelimit", "30", CVAR_ARCHIVE }, { &ui_dd_friendly, "ui_dd_friendly", "0", CVAR_ARCHIVE }, { &ui_dom_capturelimit, "ui_dom_capturelimit", "500", CVAR_ARCHIVE }, { &ui_dom_timelimit, "ui_dom_timelimit", "30", CVAR_ARCHIVE }, { &ui_dom_friendly, "ui_dom_friendly", "0", CVAR_ARCHIVE }, { &ui_arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM }, { &ui_botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM }, { &ui_spScores1, "g_spScores1", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE | CVAR_ROM }, { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE | CVAR_LATCH }, { &ui_spSelection, "ui_spSelection", "", CVAR_ROM }, { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE }, { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE }, { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE }, { &ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, { &ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, { &ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, { &ui_marks, "cg_marks", "1", CVAR_ARCHIVE }, { &ui_server1, "server1", "", CVAR_ARCHIVE }, { &ui_server2, "server2", "", CVAR_ARCHIVE }, { &ui_server3, "server3", "", CVAR_ARCHIVE }, { &ui_server4, "server4", "", CVAR_ARCHIVE }, { &ui_server5, "server5", "", CVAR_ARCHIVE }, { &ui_server6, "server6", "", CVAR_ARCHIVE }, { &ui_server7, "server7", "", CVAR_ARCHIVE }, { &ui_server8, "server8", "", CVAR_ARCHIVE }, { &ui_server9, "server9", "", CVAR_ARCHIVE }, { &ui_server10, "server10", "", CVAR_ARCHIVE }, { &ui_server11, "server11", "", CVAR_ARCHIVE }, { &ui_server12, "server12", "", CVAR_ARCHIVE }, { &ui_server13, "server13", "", CVAR_ARCHIVE }, { &ui_server14, "server14", "", CVAR_ARCHIVE }, { &ui_server15, "server15", "", CVAR_ARCHIVE }, { &ui_server16, "server16", "", CVAR_ARCHIVE }, //{ &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, //new in beta 23: { &ui_browserOnlyHumans, "ui_browserOnlyHumans", "0", CVAR_ARCHIVE }, //new in beta 37: { &ui_setupchecked, "ui_setupchecked", "0", CVAR_ARCHIVE }, }; // bk001129 - made static to avoid aliasing static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]); /* ================= UI_RegisterCvars ================= */ void UI_RegisterCvars( void ) { int i; cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); } } /* ================= UI_UpdateCvars ================= */ void UI_UpdateCvars( void ) { int i; cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Update( cv->vmCvar ); } } /* ================== * UI_SetDefaultCvar * If the cvar is blank it will be set to value * This is only good for cvars that cannot naturally be blank ================== */ void UI_SetDefaultCvar(const char* cvar, const char* value) { if(strlen(UI_Cvar_VariableString(cvar)) == 0) trap_Cvar_Set(cvar,value); }openarena_0.8.8.orig/code/q3_ui/ui_qmenu.c0000644000175000017500000010757711656310261017164 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /********************************************************************** UI_QMENU.C Quake's menu framework system. **********************************************************************/ #include "ui_local.h" sfxHandle_t menu_in_sound; sfxHandle_t menu_move_sound; sfxHandle_t menu_out_sound; sfxHandle_t menu_buzz_sound; sfxHandle_t menu_null_sound; sfxHandle_t weaponChangeSound; static qhandle_t sliderBar; static qhandle_t sliderButton_0; static qhandle_t sliderButton_1; // Original colors /* vec4_t menu_text_color = {1.0f, 1.0f, 1.0f, 1.0f}; vec4_t menu_dim_color = {0.0f, 0.0f, 0.0f, 0.75f}; vec4_t color_black = {0.00f, 0.00f, 0.00f, 1.00f}; vec4_t color_white = {1.00f, 1.00f, 1.00f, 1.00f}; vec4_t color_yellow = {1.00f, 1.00f, 0.00f, 1.00f}; vec4_t color_blue = {0.00f, 0.00f, 1.00f, 1.00f}; vec4_t color_lightOrange = {1.00f, 0.68f, 0.00f, 1.00f }; vec4_t color_orange = {1.00f, 0.43f, 0.00f, 1.00f}; vec4_t color_red = {1.00f, 0.00f, 0.00f, 1.00f}; vec4_t color_dim = {0.00f, 0.00f, 0.00f, 0.25f}; vec4_t pulse_color = {1.00f, 1.00f, 1.00f, 1.00f}; vec4_t text_color_disabled = {0.50f, 0.50f, 0.50f, 1.00f}; // light gray vec4_t text_color_normal = {1.00f, 0.43f, 0.00f, 1.00f}; // light orange vec4_t text_color_highlight = {1.00f, 1.00f, 0.00f, 1.00f}; // bright yellow vec4_t listbar_color = {1.00f, 0.43f, 0.00f, 0.30f}; // transluscent orange vec4_t text_color_status = {1.00f, 1.00f, 1.00f, 1.00f}; // bright white */ // NEW AND IMPLOVED colors vec4_t menu_text_color = {1.0f, 1.0f, 1.0f, 1.0f}; vec4_t menu_dim_color = {0.0f, 0.0f, 0.0f, 0.75f}; vec4_t color_black = {0.00f, 0.00f, 0.00f, 1.00f}; vec4_t color_white = {1.00f, 1.00f, 1.00f, 1.00f}; vec4_t color_yellow = {1.00f, 1.00f, 0.00f, 1.00f}; vec4_t color_blue = {0.00f, 0.00f, 1.00f, 1.00f}; vec4_t color_lightOrange = {0.30f, 0.45f, 0.58f, 1.00f }; vec4_t color_orange = {0.30f, 0.45f, 0.58f, 1.00f}; vec4_t color_red = {0.55f, 0.65f, 0.73f, 1.00f}; vec4_t color_dim = {0.00f, 0.00f, 0.00f, 0.25f}; // current color scheme vec4_t pulse_color = {1.00f, 1.00f, 1.00f, 1.00f}; vec4_t text_color_disabled = {0.35f, 0.24f, 0.29f, 1.00f}; // light gray vec4_t text_color_normal = {0.30f, 0.45f, 0.58f, 1.00f}; // light orange vec4_t text_color_highlight = {0.76f, 0.89f, 0.93f, 1.00f}; // bright yellow vec4_t listbar_color = {0.13f, 0.26f, 0.38f, 0.30f}; // transluscent orange vec4_t text_color_status = {1.00f, 1.00f, 1.00f, 1.00f}; // bright white // action widget static void Action_Init( menuaction_s *a ); static void Action_Draw( menuaction_s *a ); // radio button widget static void RadioButton_Init( menuradiobutton_s *rb ); static void RadioButton_Draw( menuradiobutton_s *rb ); static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key ); // slider widget static void Slider_Init( menuslider_s *s ); static sfxHandle_t Slider_Key( menuslider_s *s, int key ); static void Slider_Draw( menuslider_s *s ); // spin control widget static void SpinControl_Init( menulist_s *s ); static void SpinControl_Draw( menulist_s *s ); static sfxHandle_t SpinControl_Key( menulist_s *l, int key ); // text widget static void Text_Init( menutext_s *b ); static void Text_Draw( menutext_s *b ); // scrolllist widget static void ScrollList_Init( menulist_s *l ); sfxHandle_t ScrollList_Key( menulist_s *l, int key ); // proportional text widget static void PText_Init( menutext_s *b ); static void PText_Draw( menutext_s *b ); // proportional banner text widget static void BText_Init( menutext_s *b ); static void BText_Draw( menutext_s *b ); /* ================= Text_Init ================= */ static void Text_Init( menutext_s *t ) { t->generic.flags |= QMF_INACTIVE; } /* ================= Text_Draw ================= */ static void Text_Draw( menutext_s *t ) { int x; int y; char buff[512]; float* color; x = t->generic.x; y = t->generic.y; buff[0] = '\0'; // possible label if (t->generic.name) strcpy(buff,t->generic.name); // possible value if (t->string) strcat(buff,t->string); if (t->generic.flags & QMF_GRAYED) color = text_color_disabled; else color = t->color; UI_DrawString( x, y, buff, t->style, color ); } /* ================= BText_Init ================= */ static void BText_Init( menutext_s *t ) { t->generic.flags |= QMF_INACTIVE; } /* ================= BText_Draw ================= */ static void BText_Draw( menutext_s *t ) { int x; int y; float* color; x = t->generic.x; y = t->generic.y; if (t->generic.flags & QMF_GRAYED) color = text_color_disabled; else color = t->color; UI_DrawBannerString( x, y, t->string, t->style, color ); } /* ================= PText_Init ================= */ static void PText_Init( menutext_s *t ) { int x; int y; int w; int h; float sizeScale; sizeScale = UI_ProportionalSizeScale( t->style ); x = t->generic.x; y = t->generic.y; w = UI_ProportionalStringWidth( t->string ) * sizeScale; h = PROP_HEIGHT * sizeScale; if( t->generic.flags & QMF_RIGHT_JUSTIFY ) { x -= w; } else if( t->generic.flags & QMF_CENTER_JUSTIFY ) { x -= w / 2; } t->generic.left = x - PROP_GAP_WIDTH * sizeScale; t->generic.right = x + w + PROP_GAP_WIDTH * sizeScale; t->generic.top = y; t->generic.bottom = y + h; } /* ================= PText_Draw ================= */ static void PText_Draw( menutext_s *t ) { int x; int y; float * color; int style; x = t->generic.x; y = t->generic.y; if (t->generic.flags & QMF_GRAYED) color = text_color_disabled; else color = t->color; style = t->style; if( t->generic.flags & QMF_PULSEIFFOCUS ) { if( Menu_ItemAtCursor( t->generic.parent ) == t ) { style |= UI_PULSE; } else { style |= UI_INVERSE; } } UI_DrawProportionalString( x, y, t->string, style, color ); } /* ================= Bitmap_Init ================= */ void Bitmap_Init( menubitmap_s *b ) { int x; int y; int w; int h; x = b->generic.x; y = b->generic.y; w = b->width; h = b->height; if( w < 0 ) { w = -w; } if( h < 0 ) { h = -h; } if (b->generic.flags & QMF_RIGHT_JUSTIFY) { x = x - w; } else if (b->generic.flags & QMF_CENTER_JUSTIFY) { x = x - w/2; } b->generic.left = x; b->generic.right = x + w; b->generic.top = y; b->generic.bottom = y + h; b->shader = 0; b->focusshader = 0; } /* ================= Bitmap_Draw ================= */ void Bitmap_Draw( menubitmap_s *b ) { float x; float y; float w; float h; vec4_t tempcolor; float* color; x = b->generic.x; y = b->generic.y; w = b->width; h = b->height; if (b->generic.flags & QMF_RIGHT_JUSTIFY) { x = x - w; } else if (b->generic.flags & QMF_CENTER_JUSTIFY) { x = x - w/2; } // used to refresh shader if (b->generic.name && !b->shader) { b->shader = trap_R_RegisterShaderNoMip( b->generic.name ); if (!b->shader && b->errorpic) b->shader = trap_R_RegisterShaderNoMip( b->errorpic ); } if (b->focuspic && !b->focusshader) b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic ); if (b->generic.flags & QMF_GRAYED) { if (b->shader) { trap_R_SetColor( colorMdGrey ); UI_DrawHandlePic( x, y, w, h, b->shader ); trap_R_SetColor( NULL ); } } else { if (b->shader) UI_DrawHandlePic( x, y, w, h, b->shader ); // bk001204 - parentheses if ( ( (b->generic.flags & QMF_PULSE) || (b->generic.flags & QMF_PULSEIFFOCUS) ) && (Menu_ItemAtCursor( b->generic.parent ) == b)) { if (b->focuscolor) { tempcolor[0] = b->focuscolor[0]; tempcolor[1] = b->focuscolor[1]; tempcolor[2] = b->focuscolor[2]; color = tempcolor; } else color = pulse_color; color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR); trap_R_SetColor( color ); UI_DrawHandlePic( x, y, w, h, b->focusshader ); trap_R_SetColor( NULL ); } else if ((b->generic.flags & QMF_HIGHLIGHT) || ((b->generic.flags & QMF_HIGHLIGHT_IF_FOCUS) && (Menu_ItemAtCursor( b->generic.parent ) == b))) { if (b->focuscolor) { trap_R_SetColor( b->focuscolor ); UI_DrawHandlePic( x, y, w, h, b->focusshader ); trap_R_SetColor( NULL ); } else UI_DrawHandlePic( x, y, w, h, b->focusshader ); } } } /* ================= Action_Init ================= */ static void Action_Init( menuaction_s *a ) { int len; // calculate bounds if (a->generic.name) len = strlen(a->generic.name); else len = 0; // left justify text a->generic.left = a->generic.x; a->generic.right = a->generic.x + len*BIGCHAR_WIDTH; a->generic.top = a->generic.y; a->generic.bottom = a->generic.y + BIGCHAR_HEIGHT; } /* ================= Action_Draw ================= */ static void Action_Draw( menuaction_s *a ) { int x, y; int style; float* color; style = 0; color = menu_text_color; if ( a->generic.flags & QMF_GRAYED ) { color = text_color_disabled; } else if (( a->generic.flags & QMF_PULSEIFFOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition )) { color = text_color_highlight; style = UI_PULSE; } else if (( a->generic.flags & QMF_HIGHLIGHT_IF_FOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition )) { color = text_color_highlight; } else if ( a->generic.flags & QMF_BLINK ) { style = UI_BLINK; color = text_color_highlight; } x = a->generic.x; y = a->generic.y; UI_DrawString( x, y, a->generic.name, UI_LEFT|style, color ); if ( a->generic.parent->cursor == a->generic.menuPosition ) { // draw cursor UI_DrawChar( x - BIGCHAR_WIDTH, y, 13, UI_LEFT|UI_BLINK, color); } } /* ================= RadioButton_Init ================= */ static void RadioButton_Init( menuradiobutton_s *rb ) { int len; // calculate bounds if (rb->generic.name) len = strlen(rb->generic.name); else len = 0; rb->generic.left = rb->generic.x - (len+1)*SMALLCHAR_WIDTH; rb->generic.right = rb->generic.x + 6*SMALLCHAR_WIDTH; rb->generic.top = rb->generic.y; rb->generic.bottom = rb->generic.y + SMALLCHAR_HEIGHT; } /* ================= RadioButton_Key ================= */ static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key ) { switch (key) { case K_MOUSE1: if (!(rb->generic.flags & QMF_HASMOUSEFOCUS)) break; case K_JOY1: case K_JOY2: case K_JOY3: case K_JOY4: case K_ENTER: case K_LEFTARROW: case K_RIGHTARROW: rb->curvalue = !rb->curvalue; if ( rb->generic.callback ) rb->generic.callback( rb, QM_ACTIVATED ); return (menu_move_sound); } // key not handled return 0; } /* ================= RadioButton_Draw ================= */ static void RadioButton_Draw( menuradiobutton_s *rb ) { int x; int y; float *color; int style; qboolean focus; x = rb->generic.x; y = rb->generic.y; focus = (rb->generic.parent->cursor == rb->generic.menuPosition); if ( rb->generic.flags & QMF_GRAYED ) { color = text_color_disabled; style = UI_LEFT|UI_SMALLFONT; } else if ( focus ) { color = text_color_highlight; style = UI_LEFT|UI_PULSE|UI_SMALLFONT; } else { color = text_color_normal; style = UI_LEFT|UI_SMALLFONT; } if ( focus ) { // draw cursor UI_FillRect( rb->generic.left, rb->generic.top, rb->generic.right-rb->generic.left+1, rb->generic.bottom-rb->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color); } if ( rb->generic.name ) UI_DrawString( x - SMALLCHAR_WIDTH, y, rb->generic.name, UI_RIGHT|UI_SMALLFONT, color ); if ( !rb->curvalue ) { UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_off); UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "off", style, color ); } else { UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_on ); UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "on", style, color ); } } /* ================= Slider_Init ================= */ static void Slider_Init( menuslider_s *s ) { int len; // calculate bounds if (s->generic.name) len = strlen(s->generic.name); else len = 0; s->generic.left = s->generic.x - (len+1)*SMALLCHAR_WIDTH; s->generic.right = s->generic.x + (SLIDER_RANGE+2+1)*SMALLCHAR_WIDTH; s->generic.top = s->generic.y; s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT; } /* ================= Slider_Key ================= */ static sfxHandle_t Slider_Key( menuslider_s *s, int key ) { sfxHandle_t sound; int x; int oldvalue; switch (key) { case K_MOUSE1: x = uis.cursorx - s->generic.x - 2*SMALLCHAR_WIDTH; oldvalue = s->curvalue; s->curvalue = (x/(float)(SLIDER_RANGE*SMALLCHAR_WIDTH)) * (s->maxvalue-s->minvalue) + s->minvalue; if (s->curvalue < s->minvalue) s->curvalue = s->minvalue; else if (s->curvalue > s->maxvalue) s->curvalue = s->maxvalue; if (s->curvalue != oldvalue) sound = menu_move_sound; else sound = 0; break; case K_LEFTARROW: if (s->curvalue > s->minvalue) { s->curvalue--; sound = menu_move_sound; } else sound = menu_buzz_sound; break; case K_RIGHTARROW: if (s->curvalue < s->maxvalue) { s->curvalue++; sound = menu_move_sound; } else sound = menu_buzz_sound; break; default: // key not handled sound = 0; break; } if ( sound && s->generic.callback ) s->generic.callback( s, QM_ACTIVATED ); return (sound); } #if 1 /* ================= Slider_Draw ================= */ static void Slider_Draw( menuslider_s *s ) { int x; int y; int style; float *color; int button; qboolean focus; x = s->generic.x; y = s->generic.y; focus = (s->generic.parent->cursor == s->generic.menuPosition); if( s->generic.flags & QMF_GRAYED ) { color = text_color_disabled; style = UI_SMALLFONT; } else if( focus ) { color = text_color_highlight; style = UI_SMALLFONT | UI_PULSE; } else { color = text_color_normal; style = UI_SMALLFONT; } // draw label UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color ); // draw slider UI_SetColor( color ); UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y, 96, 16, sliderBar ); UI_SetColor( NULL ); // clamp thumb if( s->maxvalue > s->minvalue ) { s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue ); if( s->range < 0 ) { s->range = 0; } else if( s->range > 1) { s->range = 1; } } else { s->range = 0; } // draw thumb if( style & UI_PULSE) { button = sliderButton_1; } else { button = sliderButton_0; } UI_DrawHandlePic( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ) - 2, y - 2, 12, 20, button ); } #else /* ================= Slider_Draw ================= */ static void Slider_Draw( menuslider_s *s ) { float *color; int style; int i; int x; int y; qboolean focus; x = s->generic.x; y = s->generic.y; focus = (s->generic.parent->cursor == s->generic.menuPosition); style = UI_SMALLFONT; if ( s->generic.flags & QMF_GRAYED ) { color = text_color_disabled; } else if (focus) { color = text_color_highlight; style |= UI_PULSE; } else { color = text_color_normal; } if ( focus ) { // draw cursor UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color); } // draw label UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color ); // draw slider UI_DrawChar( x + SMALLCHAR_WIDTH, y, 128, UI_LEFT|style, color); for ( i = 0; i < SLIDER_RANGE; i++ ) UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 129, UI_LEFT|style, color); UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 130, UI_LEFT|style, color); // clamp thumb if (s->maxvalue > s->minvalue) { s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue ); if ( s->range < 0) s->range = 0; else if ( s->range > 1) s->range = 1; } else s->range = 0; // draw thumb if (style & UI_PULSE) { style &= ~UI_PULSE; style |= UI_BLINK; } UI_DrawChar( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ), y, 131, UI_LEFT|style, color); } #endif /* ================= SpinControl_Init ================= */ static void SpinControl_Init( menulist_s *s ) { int len; int l; const char* str; if (s->generic.name) len = strlen(s->generic.name) * SMALLCHAR_WIDTH; else len = 0; s->generic.left = s->generic.x - SMALLCHAR_WIDTH - len; len = s->numitems = 0; while ( (str = s->itemnames[s->numitems]) != 0 ) { l = strlen(str); if (l > len) len = l; s->numitems++; } s->generic.top = s->generic.y; s->generic.right = s->generic.x + (len+1)*SMALLCHAR_WIDTH; s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT; } /* ================= SpinControl_Key ================= */ static sfxHandle_t SpinControl_Key( menulist_s *s, int key ) { sfxHandle_t sound; sound = 0; switch (key) { case K_MOUSE1: s->curvalue++; if (s->curvalue >= s->numitems) s->curvalue = 0; sound = menu_move_sound; break; case K_LEFTARROW: if (s->curvalue > 0) { s->curvalue--; sound = menu_move_sound; } else sound = menu_buzz_sound; break; case K_RIGHTARROW: if (s->curvalue < s->numitems-1) { s->curvalue++; sound = menu_move_sound; } else sound = menu_buzz_sound; break; } if ( sound && s->generic.callback ) s->generic.callback( s, QM_ACTIVATED ); return (sound); } /* ================= SpinControl_Draw ================= */ static void SpinControl_Draw( menulist_s *s ) { float *color; int x,y; int style; qboolean focus; x = s->generic.x; y = s->generic.y; style = UI_SMALLFONT; focus = (s->generic.parent->cursor == s->generic.menuPosition); if ( s->generic.flags & QMF_GRAYED ) color = text_color_disabled; else if ( focus ) { color = text_color_highlight; style |= UI_PULSE; } else if ( s->generic.flags & QMF_BLINK ) { color = text_color_highlight; style |= UI_BLINK; } else color = text_color_normal; if ( focus ) { // draw cursor UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color); } UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color ); UI_DrawString( x + SMALLCHAR_WIDTH, y, s->itemnames[s->curvalue], style|UI_LEFT, color ); } /* ================= ScrollList_Init ================= */ static void ScrollList_Init( menulist_s *l ) { int w; l->oldvalue = 0; l->curvalue = 0; l->top = 0; if( !l->columns ) { l->columns = 1; l->seperation = 0; } else if( !l->seperation ) { l->seperation = 3; } w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH; l->generic.left = l->generic.x; l->generic.top = l->generic.y; l->generic.right = l->generic.x + w; l->generic.bottom = l->generic.y + l->height * SMALLCHAR_HEIGHT; if( l->generic.flags & QMF_CENTER_JUSTIFY ) { l->generic.left -= w / 2; l->generic.right -= w / 2; } } /* ================= ScrollList_Key ================= */ sfxHandle_t ScrollList_Key( menulist_s *l, int key ) { int x; int y; int w; int i; int j; int c; int cursorx; int cursory; int column; int index; switch (key) { case K_MOUSE1: if (l->generic.flags & QMF_HASMOUSEFOCUS) { // check scroll region x = l->generic.x; y = l->generic.y; w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH; if( l->generic.flags & QMF_CENTER_JUSTIFY ) { x -= w / 2; } if (UI_CursorInRect( x, y, w, l->height*SMALLCHAR_HEIGHT )) { cursorx = (uis.cursorx - x)/SMALLCHAR_WIDTH; column = cursorx / (l->width + l->seperation); cursory = (uis.cursory - y)/SMALLCHAR_HEIGHT; index = column * l->height + cursory; if (l->top + index < l->numitems) { l->oldvalue = l->curvalue; l->curvalue = l->top + index; if (l->oldvalue != l->curvalue && l->generic.callback) { l->generic.callback( l, QM_GOTFOCUS ); return (menu_move_sound); } } } // absorbed, silent sound effect return (menu_null_sound); } break; case K_HOME: l->oldvalue = l->curvalue; l->curvalue = 0; l->top = 0; if (l->oldvalue != l->curvalue && l->generic.callback) { l->generic.callback( l, QM_GOTFOCUS ); return (menu_move_sound); } return (menu_buzz_sound); case K_END: l->oldvalue = l->curvalue; l->curvalue = l->numitems-1; if( l->columns > 1 ) { c = (l->curvalue / l->height + 1) * l->height; l->top = c - (l->columns * l->height); } else { l->top = l->curvalue - (l->height - 1); } if (l->top < 0) l->top = 0; if (l->oldvalue != l->curvalue && l->generic.callback) { l->generic.callback( l, QM_GOTFOCUS ); return (menu_move_sound); } return (menu_buzz_sound); case K_PGUP: if( l->columns > 1 ) { return menu_null_sound; } if (l->curvalue > 0) { l->oldvalue = l->curvalue; l->curvalue -= l->height-1; if (l->curvalue < 0) l->curvalue = 0; l->top = l->curvalue; if (l->top < 0) l->top = 0; if (l->generic.callback) l->generic.callback( l, QM_GOTFOCUS ); return (menu_move_sound); } return (menu_buzz_sound); case K_PGDN: if( l->columns > 1 ) { return menu_null_sound; } if (l->curvalue < l->numitems-1) { l->oldvalue = l->curvalue; l->curvalue += l->height-1; if (l->curvalue > l->numitems-1) l->curvalue = l->numitems-1; l->top = l->curvalue - (l->height-1); if (l->top < 0) l->top = 0; if (l->generic.callback) l->generic.callback( l, QM_GOTFOCUS ); return (menu_move_sound); } return (menu_buzz_sound); case K_UPARROW: if( l->curvalue == 0 ) { return menu_buzz_sound; } l->oldvalue = l->curvalue; l->curvalue--; if( l->curvalue < l->top ) { if( l->columns == 1 ) { l->top--; } else { l->top -= l->height; } } if( l->generic.callback ) { l->generic.callback( l, QM_GOTFOCUS ); } return (menu_move_sound); case K_DOWNARROW: if( l->curvalue == l->numitems - 1 ) { return menu_buzz_sound; } l->oldvalue = l->curvalue; l->curvalue++; if( l->curvalue >= l->top + l->columns * l->height ) { if( l->columns == 1 ) { l->top++; } else { l->top += l->height; } } if( l->generic.callback ) { l->generic.callback( l, QM_GOTFOCUS ); } return menu_move_sound; case K_LEFTARROW: if( l->columns == 1 ) { return menu_null_sound; } if( l->curvalue < l->height ) { return menu_buzz_sound; } l->oldvalue = l->curvalue; l->curvalue -= l->height; if( l->curvalue < l->top ) { l->top -= l->height; } if( l->generic.callback ) { l->generic.callback( l, QM_GOTFOCUS ); } return menu_move_sound; case K_RIGHTARROW: if( l->columns == 1 ) { return menu_null_sound; } c = l->curvalue + l->height; if( c >= l->numitems ) { return menu_buzz_sound; } l->oldvalue = l->curvalue; l->curvalue = c; if( l->curvalue > l->top + l->columns * l->height - 1 ) { l->top += l->height; } if( l->generic.callback ) { l->generic.callback( l, QM_GOTFOCUS ); } return menu_move_sound; } // cycle look for ascii key inside list items if ( !Q_isprint( key ) ) return (0); // force to lower for case insensitive compare if ( Q_isupper( key ) ) { key -= 'A' - 'a'; } // iterate list items for (i=1; i<=l->numitems; i++) { j = (l->curvalue + i) % l->numitems; c = l->itemnames[j][0]; if ( Q_isupper( c ) ) { c -= 'A' - 'a'; } if (c == key) { // set current item, mimic windows listbox scroll behavior if (j < l->top) { // behind top most item, set this as new top l->top = j; } else if (j > l->top+l->height-1) { // past end of list box, do page down l->top = (j+1) - l->height; } if (l->curvalue != j) { l->oldvalue = l->curvalue; l->curvalue = j; if (l->generic.callback) l->generic.callback( l, QM_GOTFOCUS ); return ( menu_move_sound ); } return (menu_buzz_sound); } } return (menu_buzz_sound); } /* ================= ScrollList_Draw ================= */ void ScrollList_Draw( menulist_s *l ) { int x; int u; int y; int i; int base; int column; float* color; qboolean hasfocus; int style; hasfocus = (l->generic.parent->cursor == l->generic.menuPosition); x = l->generic.x; for( column = 0; column < l->columns; column++ ) { y = l->generic.y; base = l->top + column * l->height; for( i = base; i < base + l->height; i++) { if (i >= l->numitems) break; if (i == l->curvalue) { u = x - 2; if( l->generic.flags & QMF_CENTER_JUSTIFY ) { u -= (l->width * SMALLCHAR_WIDTH) / 2 + 1; } UI_FillRect(u,y,l->width*SMALLCHAR_WIDTH,SMALLCHAR_HEIGHT+2,listbar_color); color = text_color_highlight; if (hasfocus) style = UI_PULSE|UI_LEFT|UI_SMALLFONT; else style = UI_LEFT|UI_SMALLFONT; } else { color = text_color_normal; style = UI_LEFT|UI_SMALLFONT; } if( l->generic.flags & QMF_CENTER_JUSTIFY ) { style |= UI_CENTER; } UI_DrawString( x, y, l->itemnames[i], style, color); y += SMALLCHAR_HEIGHT; } x += (l->width + l->seperation) * SMALLCHAR_WIDTH; } } /* ================= Menu_AddItem ================= */ void Menu_AddItem( menuframework_s *menu, void *item ) { menucommon_s *itemptr; if (menu->nitems >= MAX_MENUITEMS) trap_Error ("Menu_AddItem: excessive items"); menu->items[menu->nitems] = item; ((menucommon_s*)menu->items[menu->nitems])->parent = menu; ((menucommon_s*)menu->items[menu->nitems])->menuPosition = menu->nitems; ((menucommon_s*)menu->items[menu->nitems])->flags &= ~QMF_HASMOUSEFOCUS; // perform any item specific initializations itemptr = (menucommon_s*)item; if (!(itemptr->flags & QMF_NODEFAULTINIT)) { switch (itemptr->type) { case MTYPE_ACTION: Action_Init((menuaction_s*)item); break; case MTYPE_FIELD: MenuField_Init((menufield_s*)item); break; case MTYPE_SPINCONTROL: SpinControl_Init((menulist_s*)item); break; case MTYPE_RADIOBUTTON: RadioButton_Init((menuradiobutton_s*)item); break; case MTYPE_SLIDER: Slider_Init((menuslider_s*)item); break; case MTYPE_BITMAP: Bitmap_Init((menubitmap_s*)item); break; case MTYPE_TEXT: Text_Init((menutext_s*)item); break; case MTYPE_SCROLLLIST: ScrollList_Init((menulist_s*)item); break; case MTYPE_PTEXT: PText_Init((menutext_s*)item); break; case MTYPE_BTEXT: BText_Init((menutext_s*)item); break; default: trap_Error( va("Menu_Init: unknown type %d", itemptr->type) ); } } menu->nitems++; } /* ================= Menu_CursorMoved ================= */ void Menu_CursorMoved( menuframework_s *m ) { void (*callback)( void *self, int notification ); if (m->cursor_prev == m->cursor) return; if (m->cursor_prev >= 0 && m->cursor_prev < m->nitems) { callback = ((menucommon_s*)(m->items[m->cursor_prev]))->callback; if (callback) callback(m->items[m->cursor_prev],QM_LOSTFOCUS); } if (m->cursor >= 0 && m->cursor < m->nitems) { callback = ((menucommon_s*)(m->items[m->cursor]))->callback; if (callback) callback(m->items[m->cursor],QM_GOTFOCUS); } } /* ================= Menu_SetCursor ================= */ void Menu_SetCursor( menuframework_s *m, int cursor ) { if (((menucommon_s*)(m->items[cursor]))->flags & (QMF_GRAYED|QMF_INACTIVE)) { // cursor can't go there return; } m->cursor_prev = m->cursor; m->cursor = cursor; Menu_CursorMoved( m ); } /* ================= Menu_SetCursorToItem ================= */ void Menu_SetCursorToItem( menuframework_s *m, void* ptr ) { int i; for (i=0; initems; i++) { if (m->items[i] == ptr) { Menu_SetCursor( m, i ); return; } } } /* ** Menu_AdjustCursor ** ** This function takes the given menu, the direction, and attempts ** to adjust the menu's cursor so that it's at the next available ** slot. */ void Menu_AdjustCursor( menuframework_s *m, int dir ) { menucommon_s *item = NULL; qboolean wrapped = qfalse; wrap: while ( m->cursor >= 0 && m->cursor < m->nitems ) { item = ( menucommon_s * ) m->items[m->cursor]; if (( item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE) ) ) { m->cursor += dir; } else { break; } } if ( dir == 1 ) { if ( m->cursor >= m->nitems ) { if ( m->wrapAround ) { if ( wrapped ) { m->cursor = m->cursor_prev; return; } m->cursor = 0; wrapped = qtrue; goto wrap; } m->cursor = m->cursor_prev; } } else { if ( m->cursor < 0 ) { if ( m->wrapAround ) { if ( wrapped ) { m->cursor = m->cursor_prev; return; } m->cursor = m->nitems - 1; wrapped = qtrue; goto wrap; } m->cursor = m->cursor_prev; } } } /* ================= Menu_Draw ================= */ void Menu_Draw( menuframework_s *menu ) { int i; menucommon_s *itemptr; // draw menu for (i=0; initems; i++) { itemptr = (menucommon_s*)menu->items[i]; if (itemptr->flags & QMF_HIDDEN) continue; if (itemptr->ownerdraw) { // total subclassing, owner draws everything itemptr->ownerdraw( itemptr ); } else { switch (itemptr->type) { case MTYPE_RADIOBUTTON: RadioButton_Draw( (menuradiobutton_s*)itemptr ); break; case MTYPE_FIELD: MenuField_Draw( (menufield_s*)itemptr ); break; case MTYPE_SLIDER: Slider_Draw( (menuslider_s*)itemptr ); break; case MTYPE_SPINCONTROL: SpinControl_Draw( (menulist_s*)itemptr ); break; case MTYPE_ACTION: Action_Draw( (menuaction_s*)itemptr ); break; case MTYPE_BITMAP: Bitmap_Draw( (menubitmap_s*)itemptr ); break; case MTYPE_TEXT: Text_Draw( (menutext_s*)itemptr ); break; case MTYPE_SCROLLLIST: ScrollList_Draw( (menulist_s*)itemptr ); break; case MTYPE_PTEXT: PText_Draw( (menutext_s*)itemptr ); break; case MTYPE_BTEXT: BText_Draw( (menutext_s*)itemptr ); break; default: trap_Error( va("Menu_Draw: unknown type %d", itemptr->type) ); } } #ifndef NDEBUG if( uis.debug ) { int x; int y; int w; int h; if( !( itemptr->flags & QMF_INACTIVE ) ) { x = itemptr->left; y = itemptr->top; w = itemptr->right - itemptr->left + 1; h = itemptr->bottom - itemptr->top + 1; if (itemptr->flags & QMF_HASMOUSEFOCUS) { UI_DrawRect(x, y, w, h, colorYellow ); } else { UI_DrawRect(x, y, w, h, colorWhite ); } } } #endif } itemptr = Menu_ItemAtCursor( menu ); if ( itemptr && itemptr->statusbar) itemptr->statusbar( ( void * ) itemptr ); } /* ================= Menu_ItemAtCursor ================= */ void *Menu_ItemAtCursor( menuframework_s *m ) { if ( m->cursor < 0 || m->cursor >= m->nitems ) return NULL; return m->items[m->cursor]; } /* ================= Menu_ActivateItem ================= */ sfxHandle_t Menu_ActivateItem( menuframework_s *s, menucommon_s* item ) { if ( item->callback ) { item->callback( item, QM_ACTIVATED ); if( !( item->flags & QMF_SILENT ) ) { return menu_move_sound; } } return 0; } /* ================= Menu_DefaultKey ================= */ sfxHandle_t Menu_DefaultKey( menuframework_s *m, int key ) { sfxHandle_t sound = 0; menucommon_s *item; int cursor_prev; // menu system keys switch ( key ) { case K_MOUSE2: case K_ESCAPE: UI_PopMenu(); return menu_out_sound; } if (!m || !m->nitems) return 0; // route key stimulus to widget item = Menu_ItemAtCursor( m ); if (item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE))) { switch (item->type) { case MTYPE_SPINCONTROL: sound = SpinControl_Key( (menulist_s*)item, key ); break; case MTYPE_RADIOBUTTON: sound = RadioButton_Key( (menuradiobutton_s*)item, key ); break; case MTYPE_SLIDER: sound = Slider_Key( (menuslider_s*)item, key ); break; case MTYPE_SCROLLLIST: sound = ScrollList_Key( (menulist_s*)item, key ); break; case MTYPE_FIELD: sound = MenuField_Key( (menufield_s*)item, &key ); break; } if (sound) { // key was handled return sound; } } // default handling switch ( key ) { #ifndef NDEBUG case K_F11: uis.debug ^= 1; break; case K_F12: trap_Cmd_ExecuteText(EXEC_APPEND, "screenshot\n"); break; #endif case K_UPARROW: cursor_prev = m->cursor; m->cursor_prev = m->cursor; m->cursor--; Menu_AdjustCursor( m, -1 ); if ( cursor_prev != m->cursor ) { Menu_CursorMoved( m ); sound = menu_move_sound; } break; case K_TAB: case K_DOWNARROW: cursor_prev = m->cursor; m->cursor_prev = m->cursor; m->cursor++; Menu_AdjustCursor( m, 1 ); if ( cursor_prev != m->cursor ) { Menu_CursorMoved( m ); sound = menu_move_sound; } break; case K_MOUSE1: case K_MOUSE3: if (item) if ((item->flags & QMF_HASMOUSEFOCUS) && !(item->flags & (QMF_GRAYED|QMF_INACTIVE))) return (Menu_ActivateItem( m, item )); break; case K_JOY1: case K_JOY2: case K_JOY3: case K_JOY4: case K_AUX1: case K_AUX2: case K_AUX3: case K_AUX4: case K_AUX5: case K_AUX6: case K_AUX7: case K_AUX8: case K_AUX9: case K_AUX10: case K_AUX11: case K_AUX12: case K_AUX13: case K_AUX14: case K_AUX15: case K_AUX16: case K_KP_ENTER: case K_ENTER: if (item) if (!(item->flags & (QMF_MOUSEONLY|QMF_GRAYED|QMF_INACTIVE))) return (Menu_ActivateItem( m, item )); break; } return sound; } /* ================= Menu_Cache ================= */ void Menu_Cache( void ) { uis.charset = trap_R_RegisterShaderNoMip( "gfx/2d/bigchars" ); uis.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" ); uis.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" ); uis.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" ); uis.cursor = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" ); uis.rb_on = trap_R_RegisterShaderNoMip( "menu/art/switch_on" ); uis.rb_off = trap_R_RegisterShaderNoMip( "menu/art/switch_off" ); uis.whiteShader = trap_R_RegisterShaderNoMip( "white" ); if ( uis.glconfig.hardwareType == GLHW_RAGEPRO ) { // the blend effect turns to shit with the normal uis.menuBackShader = trap_R_RegisterShaderNoMip( "menubackRagePro" ); } else { uis.menuBackShader = trap_R_RegisterShaderNoMip( "menuback_blueish" ); } uis.menuBackNoLogoShader = trap_R_RegisterShaderNoMip( "menubacknologo_blueish" ); menu_in_sound = trap_S_RegisterSound( "sound/misc/menu1.wav", qfalse ); menu_move_sound = trap_S_RegisterSound( "sound/misc/menu2.wav", qfalse ); menu_out_sound = trap_S_RegisterSound( "sound/misc/menu3.wav", qfalse ); menu_buzz_sound = trap_S_RegisterSound( "sound/misc/menu4.wav", qfalse ); weaponChangeSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse ); // need a nonzero sound, make an empty sound for this menu_null_sound = -1; sliderBar = trap_R_RegisterShaderNoMip( "menu/art/slider2" ); sliderButton_0 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_0" ); sliderButton_1 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_1" ); } openarena_0.8.8.orig/code/q3_ui/ui_setup.c0000644000175000017500000002454411656310261017167 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= SETUP MENU ======================================================================= */ #include "ui_local.h" #define SETUP_MENU_VERTICAL_SPACING 34 #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ID_CUSTOMIZEPLAYER 10 #define ID_CUSTOMIZECONTROLS 11 #define ID_SYSTEMCONFIG 12 #define ID_GAME 13 //#define ID_CDKEY 14 #define ID_LOAD 15 #define ID_SAVE 16 #define ID_DEFAULTS 17 #define ID_BACK 18 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menutext_s setupplayer; menutext_s setupcontrols; menutext_s setupsystem; menutext_s game; // menutext_s cdkey; // menutext_s load; // menutext_s save; menutext_s defaults; menubitmap_s back; } setupMenuInfo_t; static setupMenuInfo_t setupMenuInfo; /* ================= Setup_ResetDefaults_Action ================= */ static void Setup_ResetDefaults_Action( qboolean result ) { if( !result ) { return; } trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n"); trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n"); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" ); } /* ================= Setup_ResetDefaults_Draw ================= */ static void Setup_ResetDefaults_Draw( void ) { UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This will reset *ALL*", UI_CENTER|UI_SMALLFONT, color_yellow ); UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "options to their default values.", UI_CENTER|UI_SMALLFONT, color_yellow ); } /* =============== UI_SetupMenu_Event =============== */ static void UI_SetupMenu_Event( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_CUSTOMIZEPLAYER: UI_PlayerSettingsMenu(); break; case ID_CUSTOMIZECONTROLS: UI_ControlsMenu(); break; case ID_SYSTEMCONFIG: UI_GraphicsOptionsMenu(); break; case ID_GAME: UI_PreferencesMenu(); break; // case ID_CDKEY: // UI_CDKeyMenu(); // break; // case ID_LOAD: // UI_LoadConfigMenu(); // break; // case ID_SAVE: // UI_SaveConfigMenu(); // break; case ID_DEFAULTS: UI_ConfirmMenu( "SET TO DEFAULTS?", Setup_ResetDefaults_Draw, Setup_ResetDefaults_Action ); break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_SetupMenu_Init =============== */ static void UI_SetupMenu_Init( void ) { int y; UI_SetupMenu_Cache(); memset( &setupMenuInfo, 0, sizeof(setupMenuInfo) ); setupMenuInfo.menu.wrapAround = qtrue; setupMenuInfo.menu.fullscreen = qtrue; setupMenuInfo.banner.generic.type = MTYPE_BTEXT; setupMenuInfo.banner.generic.x = 320; setupMenuInfo.banner.generic.y = 16; setupMenuInfo.banner.string = "SETUP"; setupMenuInfo.banner.color = color_white; setupMenuInfo.banner.style = UI_CENTER; setupMenuInfo.framel.generic.type = MTYPE_BITMAP; setupMenuInfo.framel.generic.name = ART_FRAMEL; setupMenuInfo.framel.generic.flags = QMF_INACTIVE; setupMenuInfo.framel.generic.x = 0; setupMenuInfo.framel.generic.y = 78; setupMenuInfo.framel.width = 256; setupMenuInfo.framel.height = 329; setupMenuInfo.framer.generic.type = MTYPE_BITMAP; setupMenuInfo.framer.generic.name = ART_FRAMER; setupMenuInfo.framer.generic.flags = QMF_INACTIVE; setupMenuInfo.framer.generic.x = 376; setupMenuInfo.framer.generic.y = 76; setupMenuInfo.framer.width = 256; setupMenuInfo.framer.height = 334; y = 134; setupMenuInfo.setupplayer.generic.type = MTYPE_PTEXT; setupMenuInfo.setupplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.setupplayer.generic.x = 320; setupMenuInfo.setupplayer.generic.y = y; setupMenuInfo.setupplayer.generic.id = ID_CUSTOMIZEPLAYER; setupMenuInfo.setupplayer.generic.callback = UI_SetupMenu_Event; setupMenuInfo.setupplayer.string = "PLAYER"; setupMenuInfo.setupplayer.color = color_red; setupMenuInfo.setupplayer.style = UI_CENTER; y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.setupcontrols.generic.type = MTYPE_PTEXT; setupMenuInfo.setupcontrols.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.setupcontrols.generic.x = 320; setupMenuInfo.setupcontrols.generic.y = y; setupMenuInfo.setupcontrols.generic.id = ID_CUSTOMIZECONTROLS; setupMenuInfo.setupcontrols.generic.callback = UI_SetupMenu_Event; setupMenuInfo.setupcontrols.string = "CONTROLS"; setupMenuInfo.setupcontrols.color = color_red; setupMenuInfo.setupcontrols.style = UI_CENTER; y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.setupsystem.generic.type = MTYPE_PTEXT; setupMenuInfo.setupsystem.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.setupsystem.generic.x = 320; setupMenuInfo.setupsystem.generic.y = y; setupMenuInfo.setupsystem.generic.id = ID_SYSTEMCONFIG; setupMenuInfo.setupsystem.generic.callback = UI_SetupMenu_Event; setupMenuInfo.setupsystem.string = "SYSTEM"; setupMenuInfo.setupsystem.color = color_red; setupMenuInfo.setupsystem.style = UI_CENTER; y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.game.generic.type = MTYPE_PTEXT; setupMenuInfo.game.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.game.generic.x = 320; setupMenuInfo.game.generic.y = y; setupMenuInfo.game.generic.id = ID_GAME; setupMenuInfo.game.generic.callback = UI_SetupMenu_Event; setupMenuInfo.game.string = "GAME OPTIONS"; setupMenuInfo.game.color = color_red; setupMenuInfo.game.style = UI_CENTER; /* y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.cdkey.generic.type = MTYPE_PTEXT; setupMenuInfo.cdkey.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.cdkey.generic.x = 320; setupMenuInfo.cdkey.generic.y = y; setupMenuInfo.cdkey.generic.id = ID_CDKEY; setupMenuInfo.cdkey.generic.callback = UI_SetupMenu_Event; setupMenuInfo.cdkey.string = "CD Key"; setupMenuInfo.cdkey.color = color_red; setupMenuInfo.cdkey.style = UI_CENTER;*/ if( !trap_Cvar_VariableValue( "cl_paused" ) ) { #if 0 y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.load.generic.type = MTYPE_PTEXT; setupMenuInfo.load.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.load.generic.x = 320; setupMenuInfo.load.generic.y = y; setupMenuInfo.load.generic.id = ID_LOAD; setupMenuInfo.load.generic.callback = UI_SetupMenu_Event; setupMenuInfo.load.string = "LOAD"; setupMenuInfo.load.color = color_red; setupMenuInfo.load.style = UI_CENTER; y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.save.generic.type = MTYPE_PTEXT; setupMenuInfo.save.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.save.generic.x = 320; setupMenuInfo.save.generic.y = y; setupMenuInfo.save.generic.id = ID_SAVE; setupMenuInfo.save.generic.callback = UI_SetupMenu_Event; setupMenuInfo.save.string = "SAVE"; setupMenuInfo.save.color = color_red; setupMenuInfo.save.style = UI_CENTER; #endif y += SETUP_MENU_VERTICAL_SPACING; setupMenuInfo.defaults.generic.type = MTYPE_PTEXT; setupMenuInfo.defaults.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.defaults.generic.x = 320; setupMenuInfo.defaults.generic.y = y; setupMenuInfo.defaults.generic.id = ID_DEFAULTS; setupMenuInfo.defaults.generic.callback = UI_SetupMenu_Event; setupMenuInfo.defaults.string = "DEFAULTS"; setupMenuInfo.defaults.color = color_red; setupMenuInfo.defaults.style = UI_CENTER; } setupMenuInfo.back.generic.type = MTYPE_BITMAP; setupMenuInfo.back.generic.name = ART_BACK0; setupMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; setupMenuInfo.back.generic.id = ID_BACK; setupMenuInfo.back.generic.callback = UI_SetupMenu_Event; setupMenuInfo.back.generic.x = 0; setupMenuInfo.back.generic.y = 480-64; setupMenuInfo.back.width = 128; setupMenuInfo.back.height = 64; setupMenuInfo.back.focuspic = ART_BACK1; Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.banner ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.framel ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.framer ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupplayer ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupcontrols ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupsystem ); Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.game ); // Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.cdkey ); // Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.load ); // Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.save ); if( !trap_Cvar_VariableValue( "cl_paused" ) ) { Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.defaults ); } Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.back ); } /* ================= UI_SetupMenu_Cache ================= */ void UI_SetupMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); } /* =============== UI_SetupMenu =============== */ void UI_SetupMenu( void ) { UI_SetupMenu_Init(); UI_PushMenu( &setupMenuInfo.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_mods.c0000644000175000017500000001723511656310261016770 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/load_0" #define ART_FIGHT1 "menu/art_blueish/load_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define MAX_MODS 64 #define NAMEBUFSIZE ( MAX_MODS * 48 ) #define GAMEBUFSIZE ( MAX_MODS * 16 ) #define ID_BACK 10 #define ID_GO 11 #define ID_LIST 12 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menulist_s list; menubitmap_s back; menubitmap_s go; char description[NAMEBUFSIZE]; char fs_game[GAMEBUFSIZE]; char *descriptionPtr; char *fs_gamePtr; char *descriptionList[MAX_MODS]; char *fs_gameList[MAX_MODS]; } mods_t; static mods_t s_mods; /* =============== UI_Mods_MenuEvent =============== */ static void UI_Mods_MenuEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch ( ((menucommon_s*)ptr)->id ) { case ID_GO: trap_Cvar_Set( "fs_game", s_mods.fs_gameList[s_mods.list.curvalue] ); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); UI_PopMenu(); break; case ID_BACK: UI_PopMenu(); break; } } /* =============== UI_Mods_ParseInfos =============== */ static void UI_Mods_ParseInfos( char *modDir, char *modDesc ) { s_mods.fs_gameList[s_mods.list.numitems] = s_mods.fs_gamePtr; Q_strncpyz( s_mods.fs_gamePtr, modDir, 16 ); s_mods.descriptionList[s_mods.list.numitems] = s_mods.descriptionPtr; Q_strncpyz( s_mods.descriptionPtr, modDesc, 48 ); s_mods.list.itemnames[s_mods.list.numitems] = s_mods.descriptionPtr; s_mods.descriptionPtr += strlen( s_mods.descriptionPtr ) + 1; s_mods.fs_gamePtr += strlen( s_mods.fs_gamePtr ) + 1; s_mods.list.numitems++; } #if 0 // bk001204 - unused /* =============== UI_Mods_LoadModsFromFile =============== */ static void UI_Mods_LoadModsFromFile( char *filename ) { int len; fileHandle_t f; char buf[1024]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= sizeof(buf) ) { trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, sizeof(buf) ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); len = strlen( filename ); if( !Q_stricmp(filename + len - 4,".mod") ) { filename[len-4] = '\0'; } UI_Mods_ParseInfos( filename, buf ); } #endif /* =============== UI_Mods_LoadMods =============== */ static void UI_Mods_LoadMods( void ) { int numdirs; char dirlist[2048]; char *dirptr; char *descptr; int i; int dirlen; s_mods.list.itemnames = (const char **)s_mods.descriptionList; s_mods.descriptionPtr = s_mods.description; s_mods.fs_gamePtr = s_mods.fs_game; // always start off with baseoa s_mods.list.numitems = 1; s_mods.list.itemnames[0] = s_mods.descriptionList[0] = "OpenArena"; s_mods.fs_gameList[0] = ""; numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) ); dirptr = dirlist; for( i = 0; i < numdirs; i++ ) { dirlen = strlen( dirptr ) + 1; descptr = dirptr + dirlen; UI_Mods_ParseInfos( dirptr, descptr); dirptr += dirlen + strlen(descptr) + 1; } trap_Print( va( "%i mods parsed\n", s_mods.list.numitems ) ); if (s_mods.list.numitems > MAX_MODS) { s_mods.list.numitems = MAX_MODS; } } /* ================= UI_ModsMenu_Key ================= */ static sfxHandle_t UI_ModsMenu_Key( int key ) { menucommon_s *item; item = Menu_ItemAtCursor( &s_mods.menu ); if( key == K_MWHEELUP ) { ScrollList_Key( &s_mods.list, K_UPARROW ); } if( key == K_MWHEELDOWN ) { ScrollList_Key( &s_mods.list, K_DOWNARROW ); } return Menu_DefaultKey( &s_mods.menu, key ); } /* =============== UI_Mods_MenuInit =============== */ static void UI_Mods_MenuInit( void ) { UI_ModsMenu_Cache(); memset( &s_mods, 0 ,sizeof(mods_t) ); s_mods.menu.key = UI_ModsMenu_Key; s_mods.menu.wrapAround = qtrue; s_mods.menu.fullscreen = qtrue; s_mods.banner.generic.type = MTYPE_BTEXT; s_mods.banner.generic.x = 320; s_mods.banner.generic.y = 16; s_mods.banner.string = "MODS"; s_mods.banner.color = color_white; s_mods.banner.style = UI_CENTER; s_mods.framel.generic.type = MTYPE_BITMAP; s_mods.framel.generic.name = ART_FRAMEL; s_mods.framel.generic.flags = QMF_INACTIVE; s_mods.framel.generic.x = 0; s_mods.framel.generic.y = 78; s_mods.framel.width = 256; s_mods.framel.height = 329; s_mods.framer.generic.type = MTYPE_BITMAP; s_mods.framer.generic.name = ART_FRAMER; s_mods.framer.generic.flags = QMF_INACTIVE; s_mods.framer.generic.x = 376; s_mods.framer.generic.y = 76; s_mods.framer.width = 256; s_mods.framer.height = 334; s_mods.back.generic.type = MTYPE_BITMAP; s_mods.back.generic.name = ART_BACK0; s_mods.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_mods.back.generic.id = ID_BACK; s_mods.back.generic.callback = UI_Mods_MenuEvent; s_mods.back.generic.x = 0; s_mods.back.generic.y = 480-64; s_mods.back.width = 128; s_mods.back.height = 64; s_mods.back.focuspic = ART_BACK1; s_mods.go.generic.type = MTYPE_BITMAP; s_mods.go.generic.name = ART_FIGHT0; s_mods.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_mods.go.generic.id = ID_GO; s_mods.go.generic.callback = UI_Mods_MenuEvent; s_mods.go.generic.x = 640; s_mods.go.generic.y = 480-64; s_mods.go.width = 128; s_mods.go.height = 64; s_mods.go.focuspic = ART_FIGHT1; // scan for mods s_mods.list.generic.type = MTYPE_SCROLLLIST; s_mods.list.generic.flags = QMF_PULSEIFFOCUS|QMF_CENTER_JUSTIFY; s_mods.list.generic.callback = UI_Mods_MenuEvent; s_mods.list.generic.id = ID_LIST; s_mods.list.generic.x = 320; s_mods.list.generic.y = 130; s_mods.list.width = 48; s_mods.list.height = 14; UI_Mods_LoadMods(); Menu_AddItem( &s_mods.menu, &s_mods.banner ); Menu_AddItem( &s_mods.menu, &s_mods.framel ); Menu_AddItem( &s_mods.menu, &s_mods.framer ); Menu_AddItem( &s_mods.menu, &s_mods.list ); Menu_AddItem( &s_mods.menu, &s_mods.back ); Menu_AddItem( &s_mods.menu, &s_mods.go ); } /* ================= UI_Mods_Cache ================= */ void UI_ModsMenu_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); } /* =============== UI_ModsMenu =============== */ void UI_ModsMenu( void ) { UI_Mods_MenuInit(); UI_PushMenu( &s_mods.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_loadconfig.c0000644000175000017500000001766511656310261020142 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= LOAD CONFIG MENU ============================================================================= */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_FIGHT0 "menu/art_blueish/load_0" #define ART_FIGHT1 "menu/art_blueish/load_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_ARROWS "menu/art_blueish/arrows_horz_0" #define ART_ARROWLEFT "menu/art_blueish/arrows_horz_left" #define ART_ARROWRIGHT "menu/art_blueish/arrows_horz_right" #define MAX_CONFIGS 128 #define NAMEBUFSIZE ( MAX_CONFIGS * 16 ) #define ID_BACK 10 #define ID_GO 11 #define ID_LIST 12 #define ID_LEFT 13 #define ID_RIGHT 14 #define ARROWS_WIDTH 128 #define ARROWS_HEIGHT 48 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menulist_s list; menubitmap_s arrows; menubitmap_s left; menubitmap_s right; menubitmap_s back; menubitmap_s go; char names[NAMEBUFSIZE]; char* configlist[MAX_CONFIGS]; } configs_t; static configs_t s_configs; /* =============== LoadConfig_MenuEvent =============== */ static void LoadConfig_MenuEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch ( ((menucommon_s*)ptr)->id ) { case ID_GO: trap_Cmd_ExecuteText( EXEC_APPEND, va( "exec %s\n", s_configs.list.itemnames[s_configs.list.curvalue] ) ); UI_PopMenu(); break; case ID_BACK: UI_PopMenu(); break; case ID_LEFT: ScrollList_Key( &s_configs.list, K_LEFTARROW ); break; case ID_RIGHT: ScrollList_Key( &s_configs.list, K_RIGHTARROW ); break; } } /* =============== LoadConfig_MenuInit =============== */ static void LoadConfig_MenuInit( void ) { int i; int len; char *configname; UI_LoadConfig_Cache(); memset( &s_configs, 0 ,sizeof(configs_t) ); s_configs.menu.wrapAround = qtrue; s_configs.menu.fullscreen = qtrue; s_configs.banner.generic.type = MTYPE_BTEXT; s_configs.banner.generic.x = 320; s_configs.banner.generic.y = 16; s_configs.banner.string = "LOAD CONFIG"; s_configs.banner.color = color_white; s_configs.banner.style = UI_CENTER; s_configs.framel.generic.type = MTYPE_BITMAP; s_configs.framel.generic.name = ART_FRAMEL; s_configs.framel.generic.flags = QMF_INACTIVE; s_configs.framel.generic.x = 0; s_configs.framel.generic.y = 78; s_configs.framel.width = 256; s_configs.framel.height = 329; s_configs.framer.generic.type = MTYPE_BITMAP; s_configs.framer.generic.name = ART_FRAMER; s_configs.framer.generic.flags = QMF_INACTIVE; s_configs.framer.generic.x = 376; s_configs.framer.generic.y = 76; s_configs.framer.width = 256; s_configs.framer.height = 334; s_configs.arrows.generic.type = MTYPE_BITMAP; s_configs.arrows.generic.name = ART_ARROWS; s_configs.arrows.generic.flags = QMF_INACTIVE; s_configs.arrows.generic.x = 320-ARROWS_WIDTH/2; s_configs.arrows.generic.y = 400; s_configs.arrows.width = ARROWS_WIDTH; s_configs.arrows.height = ARROWS_HEIGHT; s_configs.left.generic.type = MTYPE_BITMAP; s_configs.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_configs.left.generic.x = 320-ARROWS_WIDTH/2; s_configs.left.generic.y = 400; s_configs.left.generic.id = ID_LEFT; s_configs.left.generic.callback = LoadConfig_MenuEvent; s_configs.left.width = ARROWS_WIDTH/2; s_configs.left.height = ARROWS_HEIGHT; s_configs.left.focuspic = ART_ARROWLEFT; s_configs.right.generic.type = MTYPE_BITMAP; s_configs.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_configs.right.generic.x = 320; s_configs.right.generic.y = 400; s_configs.right.generic.id = ID_RIGHT; s_configs.right.generic.callback = LoadConfig_MenuEvent; s_configs.right.width = ARROWS_WIDTH/2; s_configs.right.height = ARROWS_HEIGHT; s_configs.right.focuspic = ART_ARROWRIGHT; s_configs.back.generic.type = MTYPE_BITMAP; s_configs.back.generic.name = ART_BACK0; s_configs.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_configs.back.generic.id = ID_BACK; s_configs.back.generic.callback = LoadConfig_MenuEvent; s_configs.back.generic.x = 0; s_configs.back.generic.y = 480-64; s_configs.back.width = 128; s_configs.back.height = 64; s_configs.back.focuspic = ART_BACK1; s_configs.go.generic.type = MTYPE_BITMAP; s_configs.go.generic.name = ART_FIGHT0; s_configs.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_configs.go.generic.id = ID_GO; s_configs.go.generic.callback = LoadConfig_MenuEvent; s_configs.go.generic.x = 640; s_configs.go.generic.y = 480-64; s_configs.go.width = 128; s_configs.go.height = 64; s_configs.go.focuspic = ART_FIGHT1; // scan for configs s_configs.list.generic.type = MTYPE_SCROLLLIST; s_configs.list.generic.flags = QMF_PULSEIFFOCUS; s_configs.list.generic.callback = LoadConfig_MenuEvent; s_configs.list.generic.id = ID_LIST; s_configs.list.generic.x = 118; s_configs.list.generic.y = 130; s_configs.list.width = 16; s_configs.list.height = 14; s_configs.list.numitems = trap_FS_GetFileList( "", "cfg", s_configs.names, NAMEBUFSIZE ); s_configs.list.itemnames = (const char **)s_configs.configlist; s_configs.list.columns = 3; if (!s_configs.list.numitems) { strcpy(s_configs.names,"No Files Found."); s_configs.list.numitems = 1; //degenerate case, not selectable s_configs.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); } else if (s_configs.list.numitems > MAX_CONFIGS) s_configs.list.numitems = MAX_CONFIGS; configname = s_configs.names; for ( i = 0; i < s_configs.list.numitems; i++ ) { s_configs.list.itemnames[i] = configname; // strip extension len = strlen( configname ); if (!Q_stricmp(configname + len - 4,".cfg")) configname[len-4] = '\0'; Q_strupr(configname); configname += len + 1; } Menu_AddItem( &s_configs.menu, &s_configs.banner ); Menu_AddItem( &s_configs.menu, &s_configs.framel ); Menu_AddItem( &s_configs.menu, &s_configs.framer ); Menu_AddItem( &s_configs.menu, &s_configs.list ); Menu_AddItem( &s_configs.menu, &s_configs.arrows ); Menu_AddItem( &s_configs.menu, &s_configs.left ); Menu_AddItem( &s_configs.menu, &s_configs.right ); Menu_AddItem( &s_configs.menu, &s_configs.back ); Menu_AddItem( &s_configs.menu, &s_configs.go ); } /* ================= UI_LoadConfig_Cache ================= */ void UI_LoadConfig_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_FIGHT0 ); trap_R_RegisterShaderNoMip( ART_FIGHT1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_ARROWS ); trap_R_RegisterShaderNoMip( ART_ARROWLEFT ); trap_R_RegisterShaderNoMip( ART_ARROWRIGHT ); } /* =============== UI_LoadConfigMenu =============== */ void UI_LoadConfigMenu( void ) { LoadConfig_MenuInit(); UI_PushMenu( &s_configs.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_playermodel.c0000644000175000017500000005102411656310261020335 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "ui_local.h" #define MODEL_BACK0 "menu/art_blueish/back_0" #define MODEL_BACK1 "menu/art_blueish/back_1" #define MODEL_SELECT "menu/art/opponents_select" #define MODEL_SELECTED "menu/art/opponents_selected" #define MODEL_FRAMEL "menu/art_blueish/frame1_l" #define MODEL_FRAMER "menu/art_blueish/frame1_r" #define MODEL_PORTS "menu/art_blueish/player_models_ports" #define MODEL_ARROWS "menu/art_blueish/gs_arrows_0" #define MODEL_ARROWSL "menu/art_blueish/gs_arrows_l" #define MODEL_ARROWSR "menu/art_blueish/gs_arrows_r" #define LOW_MEMORY (5 * 1024 * 1024) static char* playermodel_artlist[] = { MODEL_BACK0, MODEL_BACK1, MODEL_SELECT, MODEL_SELECTED, MODEL_FRAMEL, MODEL_FRAMER, MODEL_PORTS, MODEL_ARROWS, MODEL_ARROWSL, MODEL_ARROWSR, NULL }; #define PLAYERGRID_COLS 4 #define PLAYERGRID_ROWS 4 #define MAX_MODELSPERPAGE (PLAYERGRID_ROWS*PLAYERGRID_COLS) #define MAX_PLAYERMODELS 256 #define ID_PLAYERPIC0 0 #define ID_PLAYERPIC1 1 #define ID_PLAYERPIC2 2 #define ID_PLAYERPIC3 3 #define ID_PLAYERPIC4 4 #define ID_PLAYERPIC5 5 #define ID_PLAYERPIC6 6 #define ID_PLAYERPIC7 7 #define ID_PLAYERPIC8 8 #define ID_PLAYERPIC9 9 #define ID_PLAYERPIC10 10 #define ID_PLAYERPIC11 11 #define ID_PLAYERPIC12 12 #define ID_PLAYERPIC13 13 #define ID_PLAYERPIC14 14 #define ID_PLAYERPIC15 15 #define ID_PREVPAGE 100 #define ID_NEXTPAGE 101 #define ID_BACK 102 typedef struct { menuframework_s menu; menubitmap_s pics[MAX_MODELSPERPAGE]; menubitmap_s picbuttons[MAX_MODELSPERPAGE]; menubitmap_s framel; menubitmap_s framer; menubitmap_s ports; menutext_s banner; menubitmap_s back; menubitmap_s player; menubitmap_s arrows; menubitmap_s left; menubitmap_s right; menutext_s modelname; menutext_s skinname; menutext_s playername; playerInfo_t playerinfo; int nummodels; char modelnames[MAX_PLAYERMODELS][128]; int modelpage; int numpages; char modelskin[64]; int selectedmodel; } playermodel_t; static playermodel_t s_playermodel; /* ================= PlayerModel_UpdateGrid ================= */ static void PlayerModel_UpdateGrid( void ) { int i; int j; j = s_playermodel.modelpage * MAX_MODELSPERPAGE; for (i=0; i 1) { if (s_playermodel.modelpage > 0) s_playermodel.left.generic.flags &= ~QMF_INACTIVE; else s_playermodel.left.generic.flags |= QMF_INACTIVE; if (s_playermodel.modelpage < s_playermodel.numpages-1) s_playermodel.right.generic.flags &= ~QMF_INACTIVE; else s_playermodel.right.generic.flags |= QMF_INACTIVE; } else { // hide left/right markers s_playermodel.left.generic.flags |= QMF_INACTIVE; s_playermodel.right.generic.flags |= QMF_INACTIVE; } } /* ================= PlayerModel_UpdateModel ================= */ static void PlayerModel_UpdateModel( void ) { vec3_t viewangles; vec3_t moveangles; memset( &s_playermodel.playerinfo, 0, sizeof(playerInfo_t) ); viewangles[YAW] = 180 - 30; viewangles[PITCH] = 0; viewangles[ROLL] = 0; VectorClear( moveangles ); UI_PlayerInfo_SetModel( &s_playermodel.playerinfo, s_playermodel.modelskin ); UI_PlayerInfo_SetInfo( &s_playermodel.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, moveangles, WP_MACHINEGUN, qfalse ); } /* ================= PlayerModel_SaveChanges ================= */ static void PlayerModel_SaveChanges( void ) { trap_Cvar_Set( "model", s_playermodel.modelskin ); trap_Cvar_Set( "headmodel", s_playermodel.modelskin ); trap_Cvar_Set( "team_model", s_playermodel.modelskin ); trap_Cvar_Set( "team_headmodel", s_playermodel.modelskin ); } /* ================= PlayerModel_MenuEvent ================= */ static void PlayerModel_MenuEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) return; switch (((menucommon_s*)ptr)->id) { case ID_PREVPAGE: if (s_playermodel.modelpage > 0) { s_playermodel.modelpage--; PlayerModel_UpdateGrid(); } break; case ID_NEXTPAGE: if (s_playermodel.modelpage < s_playermodel.numpages-1) { s_playermodel.modelpage++; PlayerModel_UpdateGrid(); } break; case ID_BACK: PlayerModel_SaveChanges(); UI_PopMenu(); break; } } /* ================= PlayerModel_MenuKey ================= */ static sfxHandle_t PlayerModel_MenuKey( int key ) { menucommon_s* m; int picnum; switch (key) { case K_KP_LEFTARROW: case K_LEFTARROW: m = Menu_ItemAtCursor(&s_playermodel.menu); picnum = m->id - ID_PLAYERPIC0; if (picnum >= 0 && picnum <= 15) { if (picnum > 0) { Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor-1); return (menu_move_sound); } else if (s_playermodel.modelpage > 0) { s_playermodel.modelpage--; Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor+15); PlayerModel_UpdateGrid(); return (menu_move_sound); } else return (menu_buzz_sound); } break; case K_KP_RIGHTARROW: case K_RIGHTARROW: m = Menu_ItemAtCursor(&s_playermodel.menu); picnum = m->id - ID_PLAYERPIC0; if (picnum >= 0 && picnum <= 15) { if ((picnum < 15) && (s_playermodel.modelpage*MAX_MODELSPERPAGE + picnum+1 < s_playermodel.nummodels)) { Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor+1); return (menu_move_sound); } else if ((picnum == 15) && (s_playermodel.modelpage < s_playermodel.numpages-1)) { s_playermodel.modelpage++; Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor-15); PlayerModel_UpdateGrid(); return (menu_move_sound); } else return (menu_buzz_sound); } break; case K_MOUSE2: case K_ESCAPE: PlayerModel_SaveChanges(); break; } return ( Menu_DefaultKey( &s_playermodel.menu, key ) ); } /* ================= PlayerModel_PicEvent ================= */ static void PlayerModel_PicEvent( void* ptr, int event ) { int modelnum; int maxlen; char* buffptr; char* pdest; int i; if (event != QM_ACTIVATED) return; for (i=0; iid - ID_PLAYERPIC0; s_playermodel.pics[i].generic.flags |= QMF_HIGHLIGHT; s_playermodel.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS; // get model and strip icon_ modelnum = s_playermodel.modelpage*MAX_MODELSPERPAGE + i; buffptr = s_playermodel.modelnames[modelnum] + strlen("models/players/"); pdest = strstr(buffptr,"icon_"); if (pdest) { // track the whole model/skin name Q_strncpyz(s_playermodel.modelskin,buffptr,pdest-buffptr+1); strcat(s_playermodel.modelskin,pdest + 5); // seperate the model name maxlen = pdest-buffptr; if (maxlen > 16) maxlen = 16; Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen ); Q_strupr( s_playermodel.modelname.string ); // seperate the skin name maxlen = strlen(pdest+5)+1; if (maxlen > 16) maxlen = 16; Q_strncpyz( s_playermodel.skinname.string, pdest+5, maxlen ); Q_strupr( s_playermodel.skinname.string ); s_playermodel.selectedmodel = modelnum; if( trap_MemoryRemaining() > LOW_MEMORY ) { PlayerModel_UpdateModel(); } } } /* ================= PlayerModel_DrawPlayer ================= */ static void PlayerModel_DrawPlayer( void *self ) { menubitmap_s* b; b = (menubitmap_s*) self; if( trap_MemoryRemaining() <= LOW_MEMORY ) { UI_DrawProportionalString( b->generic.x, b->generic.y + b->height / 2, "LOW MEMORY", UI_LEFT, color_red ); return; } UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_playermodel.playerinfo, uis.realtime/2 ); } /* ================= PlayerModel_BuildList ================= */ static void PlayerModel_BuildList( void ) { int numdirs; int numfiles; char dirlist[2048]; char filelist[2048]; char skinname[MAX_QPATH]; char* dirptr; char* fileptr; int i; int j; int dirlen; int filelen; qboolean precache; precache = trap_Cvar_VariableValue("com_buildscript"); s_playermodel.modelpage = 0; s_playermodel.nummodels = 0; // iterate directory of all player models numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 2048 ); dirptr = dirlist; for (i=0; i= MAX_PLAYERMODELS) // return; } if( precache ) { trap_S_RegisterSound( va( "sound/player/announce/%s_wins.wav", skinname), qfalse ); } } } //APSFIXME - Degenerate no models case s_playermodel.numpages = s_playermodel.nummodels/MAX_MODELSPERPAGE; if (s_playermodel.nummodels % MAX_MODELSPERPAGE) s_playermodel.numpages++; } /* ================= PlayerModel_SetMenuItems ================= */ static void PlayerModel_SetMenuItems( void ) { int i; int maxlen; char modelskin[64]; char* buffptr; char* pdest; // name trap_Cvar_VariableStringBuffer( "name", s_playermodel.playername.string, 16 ); Q_CleanStr( s_playermodel.playername.string ); // model trap_Cvar_VariableStringBuffer( "model", s_playermodel.modelskin, 64 ); // use default skin if none is set if (!strchr(s_playermodel.modelskin, '/')) { Q_strcat(s_playermodel.modelskin, 64, "/default"); } // find model in our list for (i=0; i 16) maxlen = 16; Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen ); Q_strupr( s_playermodel.modelname.string ); // seperate the skin name maxlen = strlen(pdest+5)+1; if (maxlen > 16) maxlen = 16; Q_strncpyz( s_playermodel.skinname.string, pdest+5, maxlen ); Q_strupr( s_playermodel.skinname.string ); break; } } } /* ================= PlayerModel_MenuInit ================= */ static void PlayerModel_MenuInit( void ) { int i; int j; int k; int x; int y; static char playername[32]; static char modelname[32]; static char skinname[32]; // zero set all our globals memset( &s_playermodel, 0 ,sizeof(playermodel_t) ); PlayerModel_Cache(); s_playermodel.menu.key = PlayerModel_MenuKey; s_playermodel.menu.wrapAround = qtrue; s_playermodel.menu.fullscreen = qtrue; s_playermodel.banner.generic.type = MTYPE_BTEXT; s_playermodel.banner.generic.x = 320; s_playermodel.banner.generic.y = 16; s_playermodel.banner.string = "PLAYER MODEL"; s_playermodel.banner.color = color_white; s_playermodel.banner.style = UI_CENTER; s_playermodel.framel.generic.type = MTYPE_BITMAP; s_playermodel.framel.generic.name = MODEL_FRAMEL; s_playermodel.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_playermodel.framel.generic.x = 0; s_playermodel.framel.generic.y = 78; s_playermodel.framel.width = 256; s_playermodel.framel.height = 329; s_playermodel.framer.generic.type = MTYPE_BITMAP; s_playermodel.framer.generic.name = MODEL_FRAMER; s_playermodel.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_playermodel.framer.generic.x = 376; s_playermodel.framer.generic.y = 76; s_playermodel.framer.width = 256; s_playermodel.framer.height = 334; s_playermodel.ports.generic.type = MTYPE_BITMAP; s_playermodel.ports.generic.name = MODEL_PORTS; s_playermodel.ports.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; s_playermodel.ports.generic.x = 50; s_playermodel.ports.generic.y = 59; s_playermodel.ports.width = 274; s_playermodel.ports.height = 274; y = 59; for (i=0,k=0; i= 0x20 && c <= 0x7E ) { *d++ = c; } s++; } *d = '\0'; return string; } /* ================= ArenaServers_MaxPing ================= */ static int ArenaServers_MaxPing( void ) { int maxPing; maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" ); if( maxPing < 100 ) { maxPing = 100; } return maxPing; } /* ================= ArenaServers_Compare ================= */ static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) { float f1; float f2; servernode_t* t1; servernode_t* t2; t1 = (servernode_t *)arg1; t2 = (servernode_t *)arg2; switch( g_sortkey ) { case SORT_HOST: return Q_stricmp( t1->hostname, t2->hostname ); case SORT_MAP: return Q_stricmp( t1->mapname, t2->mapname ); case SORT_CLIENTS: f1 = t1->maxclients - t1->numclients; if( f1 < 0 ) { f1 = 0; } f2 = t2->maxclients - t2->numclients; if( f2 < 0 ) { f2 = 0; } if( f1 < f2 ) { return 1; } if( f1 == f2 ) { return 0; } return -1; case SORT_HUMANS: f1 = t1->humanclients; f2 = t2->humanclients; if( f1 < f2 ) { return 1; } if( f1 == f2 ) { return 0; } return -1; case SORT_GAME: if( t1->gametype < t2->gametype ) { return -1; } if( t1->gametype == t2->gametype ) { return 0; } return 1; case SORT_PING: if( t1->pingtime < t2->pingtime ) { return -1; } if( t1->pingtime > t2->pingtime ) { return 1; } return Q_stricmp( t1->hostname, t2->hostname ); } return 0; } /* ================= ArenaServers_Go ================= */ static void ArenaServers_Go( void ) { servernode_t* servernode; servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode; if( servernode ) { if(servernode->needPass) { UI_SpecifyPasswordMenu( va( "connect %s\n", servernode->adrstr ), servernode->hostname ); } else trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) ); } } /* ================= ArenaServers_UpdatePicture ================= */ static void ArenaServers_UpdatePicture( void ) { static char picname[64]; servernode_t* servernodeptr; if( !g_arenaservers.list.numitems ) { g_arenaservers.mappic.generic.name = NULL; } else { servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode; Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname ); g_arenaservers.mappic.generic.name = picname; } // force shader update during draw g_arenaservers.mappic.shader = 0; } /* ================= Q_strcpyColor - This function will return the real length of the string if numChars len of character data is desired. It looks for color codes and adds 2 to the length for each combo found. This is used to make color strings show up correctly in column formatted environments. Otherwise, the columns will be off 2 * num of color codes. ================= */ int Q_strcpyColor( const char *src, char *dest, int numChars ) { int count, len; char *d; const char *s; if( !src || !dest ) { return 0; } count = len = 0; s = src; d = dest; while( *s && count < numChars ) { if( Q_IsColorString( s )) { *d++ = *s++; *d++ = *s++; len += 2; continue; } *d = *s; s++; d++; count++; len++; } // Now fill up the end of the string with space characters if needed... while( count < numChars ) { *d = ' '; //d[len] = ' '; d++; len++; count++; } return len; } /* ================= ArenaServers_UpdateMenu ================= */ static void ArenaServers_UpdateMenu( void ) { int i; int j; int count, bufAddr; char* buff; servernode_t* servernodeptr; table_t* tableptr; char *b, *pingColor; if( g_arenaservers.numqueriedservers > 0 ) { // servers found if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) { // show progress Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers); g_arenaservers.statusbar.string = "Press SPACE to stop"; qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare); } else { // all servers pinged - enable controls g_arenaservers.master.generic.flags &= ~QMF_GRAYED; g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED; g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED; g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED; g_arenaservers.onlyhumans.generic.flags &= ~QMF_GRAYED; g_arenaservers.hideprivate.generic.flags &= ~QMF_GRAYED; g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED; g_arenaservers.list.generic.flags &= ~QMF_GRAYED; g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED; g_arenaservers.go.generic.flags &= ~QMF_GRAYED; // update status bar if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { g_arenaservers.statusbar.string = quake3worldMessage; } else { g_arenaservers.statusbar.string = ""; } } } else { // no servers found if( g_arenaservers.refreshservers ) { strcpy( g_arenaservers.status.string,"Scanning For Servers." ); g_arenaservers.statusbar.string = "Press SPACE to stop"; // disable controls during refresh g_arenaservers.master.generic.flags |= QMF_GRAYED; g_arenaservers.gametype.generic.flags |= QMF_GRAYED; g_arenaservers.sortkey.generic.flags |= QMF_GRAYED; g_arenaservers.showempty.generic.flags |= QMF_GRAYED; g_arenaservers.onlyhumans.generic.flags |= QMF_GRAYED; g_arenaservers.hideprivate.generic.flags |= QMF_GRAYED; g_arenaservers.showfull.generic.flags |= QMF_GRAYED; g_arenaservers.list.generic.flags |= QMF_GRAYED; g_arenaservers.refresh.generic.flags |= QMF_GRAYED; g_arenaservers.go.generic.flags |= QMF_GRAYED; } else { if( g_arenaservers.numqueriedservers < 0 ) { strcpy(g_arenaservers.status.string,"No Response From Master Server." ); } else { strcpy(g_arenaservers.status.string,"No Servers Found." ); } // update status bar if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { g_arenaservers.statusbar.string = quake3worldMessage; } else { g_arenaservers.statusbar.string = ""; } // end of refresh - set control state g_arenaservers.master.generic.flags &= ~QMF_GRAYED; g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED; g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED; g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED; g_arenaservers.onlyhumans.generic.flags &= ~QMF_GRAYED; g_arenaservers.hideprivate.generic.flags &= ~QMF_GRAYED; g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED; g_arenaservers.list.generic.flags |= QMF_GRAYED; g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED; g_arenaservers.go.generic.flags |= QMF_GRAYED; } // zero out list box g_arenaservers.list.numitems = 0; g_arenaservers.list.curvalue = 0; g_arenaservers.list.top = 0; // update picture ArenaServers_UpdatePicture(); return; } // build list box strings - apply culling filters servernodeptr = g_arenaservers.serverlist; count = *g_arenaservers.numservers; for( i = 0, j = 0; i < count; i++, servernodeptr++ ) { tableptr = &g_arenaservers.table[j]; tableptr->servernode = servernodeptr; buff = tableptr->buff; // can only cull valid results if( !g_emptyservers && !servernodeptr->numclients ) { continue; } //If "Show only humans" and "Hide empty server" are enabled hide servers that only have bots if( !g_emptyservers && g_onlyhumans && !servernodeptr->humanclients ) { continue; } if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) { continue; } switch( g_gametype ) { case GAMES_ALL: break; case GAMES_FFA: if( servernodeptr->gametype != GT_FFA ) { continue; } break; case GAMES_TEAMPLAY: if( servernodeptr->gametype != GT_TEAM ) { continue; } break; case GAMES_TOURNEY: if( servernodeptr->gametype != GT_TOURNAMENT ) { continue; } break; case GAMES_CTF: if( servernodeptr->gametype != GT_CTF ) { continue; } break; case GAMES_1FCTF: if( servernodeptr->gametype != GT_1FCTF ) { continue; } break; case GAMES_OBELISK: if( servernodeptr->gametype != GT_OBELISK ) { continue; } break; case GAMES_HARVESTER: if( servernodeptr->gametype != GT_HARVESTER ) { continue; } break; case GAMES_ELIMINATION: if( servernodeptr->gametype != GT_ELIMINATION ) { continue; } break; case GAMES_CTF_ELIMINATION: if( servernodeptr->gametype != GT_CTF_ELIMINATION ) { continue; } break; case GAMES_LMS: if( servernodeptr->gametype != GT_LMS ) { continue; } break; case GAMES_DOUBLE_D: if( servernodeptr->gametype != GT_DOUBLE_D ) { continue; } break; case GAMES_DOM: if( servernodeptr->gametype != GT_DOMINATION ) { continue; } break; } if(g_hideprivate && servernodeptr->needPass) continue; if( servernodeptr->pingtime < servernodeptr->minPing ) { pingColor = S_COLOR_BLUE; } else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) { pingColor = S_COLOR_BLUE; } else if( servernodeptr->pingtime < 200 ) { pingColor = S_COLOR_GREEN; } else if( servernodeptr->pingtime < 400 ) { pingColor = S_COLOR_YELLOW; } else { pingColor = S_COLOR_RED; } /* Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %3s %s%3d ", servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients, servernodeptr->maxclients, servernodeptr->gamename, netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime ); //, servernodeptr->bPB ? "Yes" : "No" */ b = buff; *b++ = '^'; *b++ = '7'; bufAddr = Q_strcpyColor( servernodeptr->hostname, b, 30 ); b += bufAddr; *b++ = ' '; *b++ = '^'; *b++ = '7'; bufAddr = Q_strcpyColor( servernodeptr->mapname, b, 16 ); b += bufAddr; *b++ = ' '; if(g_onlyhumans == 0) Com_sprintf( b, 8, "%2d/%2d ", servernodeptr->numclients, servernodeptr->maxclients ); else Com_sprintf( b, 8, "%2d/%2d ", servernodeptr->humanclients, servernodeptr->maxclients ); b += 6; bufAddr = Q_strcpyColor( servernodeptr->gamename, b, 8 ); b += bufAddr; *b++ = ' '; bufAddr = Q_strcpyColor( netnames[servernodeptr->nettype], b, 3 ); b += bufAddr; *b++ = ' '; Com_sprintf( b, 12, "%s%3d ", pingColor, servernodeptr->pingtime ); j++; } g_arenaservers.list.numitems = j; g_arenaservers.list.curvalue = 0; g_arenaservers.list.top = 0; // update picture ArenaServers_UpdatePicture(); } /* ================= ArenaServers_Remove ================= */ static void ArenaServers_Remove( void ) { int i; servernode_t* servernodeptr; table_t* tableptr; if (!g_arenaservers.list.numitems) return; // remove selected item from display list // items are in scattered order due to sort and cull // perform delete on list box contents, resync all lists tableptr = &g_arenaservers.table[g_arenaservers.list.curvalue]; servernodeptr = tableptr->servernode; // find address in master list for (i=0; iadrstr)) { // delete address from master list if (i < g_arenaservers.numfavoriteaddresses-1) { // shift items up memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)* MAX_ADDRESSLENGTH ); } g_arenaservers.numfavoriteaddresses--; memset( &g_arenaservers.favoriteaddresses[g_arenaservers.numfavoriteaddresses], 0, MAX_ADDRESSLENGTH ); break; } } // find address in server list for (i=0; i= ArenaServers_MaxPing()) && (g_servertype != UIAS_FAVORITES)) { // slow global or local servers do not get entered return; } if (*g_arenaservers.numservers >= g_arenaservers.maxservers) { // list full; servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1; } else { // next slot servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers); (*g_arenaservers.numservers)++; } Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH ); Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH ); Q_CleanStrWithColor( servernodeptr->hostname ); Q_strupr( servernodeptr->hostname ); Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH ); Q_CleanStr( servernodeptr->mapname ); Q_strupr( servernodeptr->mapname ); servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") ); servernodeptr->humanclients = atoi( Info_ValueForKey( info, "g_humanplayers") ); servernodeptr->needPass = atoi( Info_ValueForKey( info, "g_needpass") ); servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") ); servernodeptr->pingtime = pingtime; servernodeptr->minPing = atoi( Info_ValueForKey( info, "minPing") ); servernodeptr->maxPing = atoi( Info_ValueForKey( info, "maxPing") ); s = Info_ValueForKey( info, "nettype" ); for (i=0; ;i++) { if (!netnames[i]) { servernodeptr->nettype = 0; break; } else if (!Q_stricmp( netnames[i], s )) { servernodeptr->nettype = i; break; } } servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype")); s = Info_ValueForKey( info, "game"); i = atoi( Info_ValueForKey( info, "gametype") ); if( i < 0 ) { i = 0; } else if( i > 11 ) { i = 12; } if( *s ) { servernodeptr->gametype = i;//-1; Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) ); } else { servernodeptr->gametype = i; Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) ); } } /* ================= ArenaServers_InsertFavorites Insert nonresponsive address book entries into display lists. ================= */ void ArenaServers_InsertFavorites( void ) { int i; int j; char info[MAX_INFO_STRING]; // resync existing results with new or deleted cvars info[0] = '\0'; Info_SetValueForKey( info, "hostname", "No Response" ); for (i=0; i= g_numfavoriteservers) { // not in list, add it ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() ); } } } /* ================= ArenaServers_LoadFavorites Load cvar address book entries into local lists. ================= */ void ArenaServers_LoadFavorites( void ) { int i; int j; int numtempitems; char emptyinfo[MAX_INFO_STRING]; char adrstr[MAX_ADDRESSLENGTH]; servernode_t templist[MAX_FAVORITESERVERS]; qboolean found; found = qfalse; emptyinfo[0] = '\0'; // copy the old memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS ); numtempitems = g_numfavoriteservers; // clear the current for sync memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS ); g_numfavoriteservers = 0; // resync existing results with new or deleted cvars for (i=0; i '9') continue; // favorite server addresses must be maintained outside refresh list // this mimics local and global netadr's stored in client // these can be fetched to fill ping list strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr ); // find this server in the old list for (j=0; j= 0) { g_arenaservers.currentping = *g_arenaservers.numservers; g_arenaservers.numqueriedservers = *g_arenaservers.numservers; } // sort qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare); ArenaServers_UpdateMenu(); } /* ================= ArenaServers_DoRefresh ================= */ static void ArenaServers_DoRefresh( void ) { int i; int j; int time; int maxPing; char adrstr[MAX_ADDRESSLENGTH]; char info[MAX_INFO_STRING]; if (uis.realtime < g_arenaservers.refreshtime) { if (g_servertype != UIAS_FAVORITES) { if (g_servertype == UIAS_LOCAL) { if (!trap_LAN_GetServerCount(g_servertype)) { return; } } if (trap_LAN_GetServerCount(g_servertype) < 0) { // still waiting for response return; } } } if (uis.realtime < g_arenaservers.nextpingtime) { // wait for time trigger return; } // trigger at 10Hz intervals g_arenaservers.nextpingtime = uis.realtime + 10; // process ping results maxPing = ArenaServers_MaxPing(); for (i=0; i maxPing) { // stale it out info[0] = '\0'; time = maxPing; } else { trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING ); } // insert ping results ArenaServers_Insert( adrstr, info, time ); // clear this query from internal list g_arenaservers.pinglist[j].adrstr[0] = '\0'; } // clear this query from external list trap_LAN_ClearPing( i ); } // get results of servers query // counts can increase as servers respond if (g_servertype == UIAS_FAVORITES) { g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses; } else { g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(g_servertype); } // if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers) // g_arenaservers.numqueriedservers = g_arenaservers.maxservers; // send ping requests in reasonable bursts // iterate ping through all found servers for (i=0; i= MAX_PINGREQUESTS) { // ping queue is full break; } // find empty slot for (j=0; j= MAX_PINGREQUESTS) // no empty slots available yet - wait for timeout break; // get an address to ping if (g_servertype == UIAS_FAVORITES) { strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] ); } else { trap_LAN_GetServerAddressString(g_servertype, g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH ); } strcpy( g_arenaservers.pinglist[j].adrstr, adrstr ); g_arenaservers.pinglist[j].start = uis.realtime; trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr ) ); // advance to next server g_arenaservers.currentping++; } if (!trap_LAN_GetPingQueueCount()) { // all pings completed ArenaServers_StopRefresh(); return; } // update the user interface with ping status ArenaServers_UpdateMenu(); } /* ================= ArenaServers_StartRefresh ================= */ static void ArenaServers_StartRefresh( void ) { int i; char myargs[32], protocol[32]; memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) ); for (i=0; i= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { switch( g_arenaservers.gametype.curvalue ) { default: case GAMES_ALL: myargs[0] = 0; break; case GAMES_FFA: strcpy( myargs, " ffa" ); break; case GAMES_TEAMPLAY: strcpy( myargs, " team" ); break; case GAMES_TOURNEY: strcpy( myargs, " tourney" ); break; case GAMES_CTF: strcpy( myargs, " ctf" ); break; case GAMES_ELIMINATION: strcpy( myargs, " elimination" ); break; case GAMES_CTF_ELIMINATION: strcpy( myargs, " ctfelimination" ); break; case GAMES_LMS: strcpy( myargs, " lms" ); break; case GAMES_DOUBLE_D: strcpy( myargs, " dd" ); break; case GAMES_DOM: strcpy( myargs, " dom" ); break; } if (g_emptyservers) { strcat(myargs, " empty"); } if (g_fullservers) { strcat(myargs, " full"); } protocol[0] = '\0'; trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) ); if (strlen(protocol)) { trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - 1, protocol, myargs )); } else { trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - 1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) ); } } } /* ================= ArenaServers_SaveChanges ================= */ void ArenaServers_SaveChanges( void ) { int i; for (i=0; i= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5) { char masterstr[2], cvarname[sizeof("sv_master1")]; while(type <= UIAS_GLOBAL5) { Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type); trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr)); if(*masterstr) break; type++; } } g_servertype = type; switch( type ) { default: case UIAS_LOCAL: g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); g_arenaservers.serverlist = g_localserverlist; g_arenaservers.numservers = &g_numlocalservers; g_arenaservers.maxservers = MAX_LOCALSERVERS; break; case UIAS_GLOBAL1: case UIAS_GLOBAL2: case UIAS_GLOBAL3: case UIAS_GLOBAL4: case UIAS_GLOBAL5: g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); g_arenaservers.serverlist = g_globalserverlist; g_arenaservers.numservers = &g_numglobalservers; g_arenaservers.maxservers = MAX_GLOBALSERVERS; break; case UIAS_FAVORITES: g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN); g_arenaservers.serverlist = g_favoriteserverlist; g_arenaservers.numservers = &g_numfavoriteservers; g_arenaservers.maxservers = MAX_FAVORITESERVERS; break; } if( !*g_arenaservers.numservers ) { ArenaServers_StartRefresh(); } else { // avoid slow operation, use existing results g_arenaservers.currentping = *g_arenaservers.numservers; g_arenaservers.numqueriedservers = *g_arenaservers.numservers; ArenaServers_UpdateMenu(); strcpy(g_arenaservers.status.string,"hit refresh to update"); } return type; } /* ================= ArenaServers_Event ================= */ static void ArenaServers_Event( void* ptr, int event ) { int id; id = ((menucommon_s*)ptr)->id; if( event != QM_ACTIVATED && id != ID_LIST ) { return; } switch( id ) { case ID_MASTER: g_arenaservers.master.curvalue = ArenaServers_SetType(g_arenaservers.master.curvalue); trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue); break; case ID_GAMETYPE: trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue ); g_gametype = g_arenaservers.gametype.curvalue; ArenaServers_UpdateMenu(); break; case ID_SORTKEY: trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue ); ArenaServers_Sort( g_arenaservers.sortkey.curvalue ); ArenaServers_UpdateMenu(); break; case ID_SHOW_FULL: trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue ); g_fullservers = g_arenaservers.showfull.curvalue; ArenaServers_UpdateMenu(); break; case ID_SHOW_EMPTY: trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue ); g_emptyservers = g_arenaservers.showempty.curvalue; ArenaServers_UpdateMenu(); break; case ID_ONLY_HUMANS: trap_Cvar_SetValue( "ui_browserOnlyHumans", g_arenaservers.onlyhumans.curvalue ); g_onlyhumans = g_arenaservers.onlyhumans.curvalue; ArenaServers_UpdateMenu(); break; case ID_HIDE_PRIVATE: //trap_Cvar_SetValue( "ui_browserHidePrivate", g_arenaservers.hideprivate.curvalue ); g_hideprivate = g_arenaservers.hideprivate.curvalue; ArenaServers_UpdateMenu(); break; case ID_LIST: if( event == QM_GOTFOCUS ) { ArenaServers_UpdatePicture(); } break; case ID_SCROLL_UP: ScrollList_Key( &g_arenaservers.list, K_UPARROW ); break; case ID_SCROLL_DOWN: ScrollList_Key( &g_arenaservers.list, K_DOWNARROW ); break; case ID_BACK: ArenaServers_StopRefresh(); ArenaServers_SaveChanges(); UI_PopMenu(); break; case ID_REFRESH: ArenaServers_StartRefresh(); break; case ID_SPECIFY: UI_SpecifyServerMenu(); break; case ID_CREATE: UI_StartServerMenu( qtrue ); break; case ID_CONNECT: ArenaServers_Go(); break; case ID_REMOVE: ArenaServers_Remove(); ArenaServers_UpdateMenu(); break; } } /* ================= ArenaServers_MenuDraw ================= */ static void ArenaServers_MenuDraw( void ) { if (g_arenaservers.refreshservers) ArenaServers_DoRefresh(); Menu_Draw( &g_arenaservers.menu ); } /* ================= ArenaServers_MenuKey ================= */ static sfxHandle_t ArenaServers_MenuKey( int key ) { if( key == K_SPACE && g_arenaservers.refreshservers ) { ArenaServers_StopRefresh(); return menu_move_sound; } if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == UIAS_FAVORITES ) && ( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) { ArenaServers_Remove(); ArenaServers_UpdateMenu(); return menu_move_sound; } if( key == K_MOUSE2 || key == K_ESCAPE ) { ArenaServers_StopRefresh(); ArenaServers_SaveChanges(); } if( key == K_MWHEELUP ) { ScrollList_Key( &g_arenaservers.list, K_UPARROW ); } if( key == K_MWHEELDOWN ) { ScrollList_Key( &g_arenaservers.list, K_DOWNARROW ); } return Menu_DefaultKey( &g_arenaservers.menu, key ); } /* ================= ArenaServers_MenuInit ================= */ static void ArenaServers_MenuInit( void ) { int i; int y; int value; static char statusbuffer[MAX_STATUSLENGTH]; // zero set all our globals memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) ); ArenaServers_Cache(); g_arenaservers.menu.fullscreen = qtrue; g_arenaservers.menu.wrapAround = qtrue; g_arenaservers.menu.draw = ArenaServers_MenuDraw; g_arenaservers.menu.key = ArenaServers_MenuKey; g_arenaservers.banner.generic.type = MTYPE_BTEXT; g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY; g_arenaservers.banner.generic.x = 320; g_arenaservers.banner.generic.y = 16; g_arenaservers.banner.string = "ARENA SERVERS"; g_arenaservers.banner.style = UI_CENTER; g_arenaservers.banner.color = color_white; y = 80-SMALLCHAR_HEIGHT; g_arenaservers.master.generic.type = MTYPE_SPINCONTROL; g_arenaservers.master.generic.name = "Servers:"; g_arenaservers.master.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.master.generic.callback = ArenaServers_Event; g_arenaservers.master.generic.id = ID_MASTER; g_arenaservers.master.generic.x = 320; g_arenaservers.master.generic.y = y; g_arenaservers.master.itemnames = master_items; y += SMALLCHAR_HEIGHT; g_arenaservers.gametype.generic.type = MTYPE_SPINCONTROL; g_arenaservers.gametype.generic.name = "Game Type:"; g_arenaservers.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.gametype.generic.callback = ArenaServers_Event; g_arenaservers.gametype.generic.id = ID_GAMETYPE; g_arenaservers.gametype.generic.x = 320; g_arenaservers.gametype.generic.y = y; g_arenaservers.gametype.itemnames = servertype_items; y += SMALLCHAR_HEIGHT; g_arenaservers.sortkey.generic.type = MTYPE_SPINCONTROL; g_arenaservers.sortkey.generic.name = "Sort By:"; g_arenaservers.sortkey.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.sortkey.generic.callback = ArenaServers_Event; g_arenaservers.sortkey.generic.id = ID_SORTKEY; g_arenaservers.sortkey.generic.x = 320; g_arenaservers.sortkey.generic.y = y; g_arenaservers.sortkey.itemnames = sortkey_items; y += SMALLCHAR_HEIGHT; g_arenaservers.showfull.generic.type = MTYPE_RADIOBUTTON; g_arenaservers.showfull.generic.name = "Show Full:"; g_arenaservers.showfull.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.showfull.generic.callback = ArenaServers_Event; g_arenaservers.showfull.generic.id = ID_SHOW_FULL; g_arenaservers.showfull.generic.x = 320; g_arenaservers.showfull.generic.y = y; y += SMALLCHAR_HEIGHT; g_arenaservers.showempty.generic.type = MTYPE_RADIOBUTTON; g_arenaservers.showempty.generic.name = "Show Empty:"; g_arenaservers.showempty.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.showempty.generic.callback = ArenaServers_Event; g_arenaservers.showempty.generic.id = ID_SHOW_EMPTY; g_arenaservers.showempty.generic.x = 320; g_arenaservers.showempty.generic.y = y; y += SMALLCHAR_HEIGHT; g_arenaservers.onlyhumans.generic.type = MTYPE_RADIOBUTTON; g_arenaservers.onlyhumans.generic.name = "Only humans:"; g_arenaservers.onlyhumans.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.onlyhumans.generic.callback = ArenaServers_Event; g_arenaservers.onlyhumans.generic.id = ID_ONLY_HUMANS; g_arenaservers.onlyhumans.generic.x = 320; g_arenaservers.onlyhumans.generic.y = y; y += SMALLCHAR_HEIGHT; g_arenaservers.hideprivate.generic.type = MTYPE_RADIOBUTTON; g_arenaservers.hideprivate.generic.name = "Hide private:"; g_arenaservers.hideprivate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; g_arenaservers.hideprivate.generic.callback = ArenaServers_Event; g_arenaservers.hideprivate.generic.id = ID_HIDE_PRIVATE; g_arenaservers.hideprivate.generic.x = 320; g_arenaservers.hideprivate.generic.y = y; y += 2 * SMALLCHAR_HEIGHT; g_arenaservers.list.generic.type = MTYPE_SCROLLLIST; g_arenaservers.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS; g_arenaservers.list.generic.id = ID_LIST; g_arenaservers.list.generic.callback = ArenaServers_Event; g_arenaservers.list.generic.x = 22; g_arenaservers.list.generic.y = y; g_arenaservers.list.width = MAX_LISTBOXWIDTH; g_arenaservers.list.height = 11; g_arenaservers.list.itemnames = (const char **)g_arenaservers.items; for( i = 0; i < MAX_LISTBOXITEMS; i++ ) { g_arenaservers.items[i] = g_arenaservers.table[i].buff; } g_arenaservers.mappic.generic.type = MTYPE_BITMAP; g_arenaservers.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; g_arenaservers.mappic.generic.x = 72; g_arenaservers.mappic.generic.y = 80; g_arenaservers.mappic.width = 128; g_arenaservers.mappic.height = 96; g_arenaservers.mappic.errorpic = ART_UNKNOWNMAP; g_arenaservers.arrows.generic.type = MTYPE_BITMAP; g_arenaservers.arrows.generic.name = ART_ARROWS0; g_arenaservers.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE; g_arenaservers.arrows.generic.callback = ArenaServers_Event; g_arenaservers.arrows.generic.x = 512+48+12; g_arenaservers.arrows.generic.y = 240-64+48; g_arenaservers.arrows.width = 64; g_arenaservers.arrows.height = 128; g_arenaservers.up.generic.type = MTYPE_BITMAP; g_arenaservers.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; g_arenaservers.up.generic.callback = ArenaServers_Event; g_arenaservers.up.generic.id = ID_SCROLL_UP; g_arenaservers.up.generic.x = 512+48+12; g_arenaservers.up.generic.y = 240-64+48; g_arenaservers.up.width = 64; g_arenaservers.up.height = 64; g_arenaservers.up.focuspic = ART_ARROWS_UP; g_arenaservers.down.generic.type = MTYPE_BITMAP; g_arenaservers.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; g_arenaservers.down.generic.callback = ArenaServers_Event; g_arenaservers.down.generic.id = ID_SCROLL_DOWN; g_arenaservers.down.generic.x = 512+48+12; g_arenaservers.down.generic.y = 240+48; g_arenaservers.down.width = 64; g_arenaservers.down.height = 64; g_arenaservers.down.focuspic = ART_ARROWS_DOWN; y = 376; g_arenaservers.status.generic.type = MTYPE_TEXT; g_arenaservers.status.generic.x = 320; g_arenaservers.status.generic.y = y; g_arenaservers.status.string = statusbuffer; g_arenaservers.status.style = UI_CENTER|UI_SMALLFONT; g_arenaservers.status.color = menu_text_color; y += SMALLCHAR_HEIGHT; g_arenaservers.statusbar.generic.type = MTYPE_TEXT; g_arenaservers.statusbar.generic.x = 320; g_arenaservers.statusbar.generic.y = y; g_arenaservers.statusbar.string = ""; g_arenaservers.statusbar.style = UI_CENTER|UI_SMALLFONT; g_arenaservers.statusbar.color = text_color_normal; g_arenaservers.remove.generic.type = MTYPE_BITMAP; g_arenaservers.remove.generic.name = ART_REMOVE0; g_arenaservers.remove.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.remove.generic.callback = ArenaServers_Event; g_arenaservers.remove.generic.id = ID_REMOVE; g_arenaservers.remove.generic.x = 450; g_arenaservers.remove.generic.y = 86; g_arenaservers.remove.width = 96; g_arenaservers.remove.height = 48; g_arenaservers.remove.focuspic = ART_REMOVE1; g_arenaservers.back.generic.type = MTYPE_BITMAP; g_arenaservers.back.generic.name = ART_BACK0; g_arenaservers.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.back.generic.callback = ArenaServers_Event; g_arenaservers.back.generic.id = ID_BACK; g_arenaservers.back.generic.x = 0; g_arenaservers.back.generic.y = 480-64; g_arenaservers.back.width = 128; g_arenaservers.back.height = 64; g_arenaservers.back.focuspic = ART_BACK1; g_arenaservers.specify.generic.type = MTYPE_BITMAP; g_arenaservers.specify.generic.name = ART_SPECIFY0; g_arenaservers.specify.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.specify.generic.callback = ArenaServers_Event; g_arenaservers.specify.generic.id = ID_SPECIFY; g_arenaservers.specify.generic.x = 128; g_arenaservers.specify.generic.y = 480-64; g_arenaservers.specify.width = 128; g_arenaservers.specify.height = 64; g_arenaservers.specify.focuspic = ART_SPECIFY1; g_arenaservers.refresh.generic.type = MTYPE_BITMAP; g_arenaservers.refresh.generic.name = ART_REFRESH0; g_arenaservers.refresh.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.refresh.generic.callback = ArenaServers_Event; g_arenaservers.refresh.generic.id = ID_REFRESH; g_arenaservers.refresh.generic.x = 256; g_arenaservers.refresh.generic.y = 480-64; g_arenaservers.refresh.width = 128; g_arenaservers.refresh.height = 64; g_arenaservers.refresh.focuspic = ART_REFRESH1; g_arenaservers.create.generic.type = MTYPE_BITMAP; g_arenaservers.create.generic.name = ART_CREATE0; g_arenaservers.create.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.create.generic.callback = ArenaServers_Event; g_arenaservers.create.generic.id = ID_CREATE; g_arenaservers.create.generic.x = 384; g_arenaservers.create.generic.y = 480-64; g_arenaservers.create.width = 128; g_arenaservers.create.height = 64; g_arenaservers.create.focuspic = ART_CREATE1; g_arenaservers.go.generic.type = MTYPE_BITMAP; g_arenaservers.go.generic.name = ART_CONNECT0; g_arenaservers.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; g_arenaservers.go.generic.callback = ArenaServers_Event; g_arenaservers.go.generic.id = ID_CONNECT; g_arenaservers.go.generic.x = 640; g_arenaservers.go.generic.y = 480-64; g_arenaservers.go.width = 128; g_arenaservers.go.height = 64; g_arenaservers.go.focuspic = ART_CONNECT1; Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.onlyhumans ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.hideprivate ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create ); Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go ); ArenaServers_LoadFavorites(); g_servertype = Com_Clamp( 0, 3, ui_browserMaster.integer ); // hack to get rid of MPlayer stuff value = g_servertype; if (value >= 1) value--; g_arenaservers.master.curvalue = value; g_gametype = Com_Clamp( 0, 12, ui_browserGameType.integer ); g_arenaservers.gametype.curvalue = g_gametype; g_sortkey = Com_Clamp( 0, 5, ui_browserSortKey.integer ); g_arenaservers.sortkey.curvalue = g_sortkey; g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer ); g_arenaservers.showfull.curvalue = g_fullservers; g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer ); g_arenaservers.showempty.curvalue = g_emptyservers; g_arenaservers.onlyhumans.curvalue = Com_Clamp( 0, 1, ui_browserOnlyHumans.integer ); g_onlyhumans = ui_browserOnlyHumans.integer; g_arenaservers.hideprivate.curvalue = 1; //Com_Clamp( 0, 1, ui_browserOnlyHumans.integer ); g_hideprivate = 1; //ui_browserOnlyHumans.integer; // force to initial state and refresh g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype); trap_Cvar_Register(NULL, "debug_protocol", "", 0 ); } /* ================= ArenaServers_Cache ================= */ void ArenaServers_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_CREATE0 ); trap_R_RegisterShaderNoMip( ART_CREATE1 ); trap_R_RegisterShaderNoMip( ART_SPECIFY0 ); trap_R_RegisterShaderNoMip( ART_SPECIFY1 ); trap_R_RegisterShaderNoMip( ART_REFRESH0 ); trap_R_RegisterShaderNoMip( ART_REFRESH1 ); trap_R_RegisterShaderNoMip( ART_CONNECT0 ); trap_R_RegisterShaderNoMip( ART_CONNECT1 ); trap_R_RegisterShaderNoMip( ART_ARROWS0 ); trap_R_RegisterShaderNoMip( ART_ARROWS_UP ); trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN ); trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP ); } /* ================= UI_ArenaServersMenu ================= */ void UI_ArenaServersMenu( void ) { ArenaServers_MenuInit(); UI_PushMenu( &g_arenaservers.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_demo2.c0000644000175000017500000002041511656310261017026 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ======================================================================= DEMOS MENU ======================================================================= */ #include "ui_local.h" #define ART_BACK0 "menu/art_blueish/back_0" #define ART_BACK1 "menu/art_blueish/back_1" #define ART_GO0 "menu/art_blueish/play_0" #define ART_GO1 "menu/art_blueish/play_1" #define ART_FRAMEL "menu/art_blueish/frame2_l" #define ART_FRAMER "menu/art_blueish/frame1_r" #define ART_ARROWS "menu/art_blueish/arrows_vert_0" #define ART_ARROWUP "menu/art_blueish/arrows_vert_top" #define ART_ARROWDN "menu/art_blueish/arrows_vert_bot" #define MAX_DEMOS 128 #define NAMEBUFSIZE ( MAX_DEMOS * 16 ) #define ID_BACK 10 #define ID_GO 11 #define ID_LIST 12 #define ID_SCROLLDN 13 #define ID_SCROLLUP 14 #define ARROWS_WIDTH 128 #define ARROWS_HEIGHT 48 typedef struct { menuframework_s menu; menutext_s banner; menubitmap_s framel; menubitmap_s framer; menulist_s list; menubitmap_s arrows; menubitmap_s left; menubitmap_s right; menubitmap_s back; menubitmap_s go; int numDemos; char names[NAMEBUFSIZE]; char *demolist[MAX_DEMOS]; } demos_t; static demos_t s_demos; /* =============== Demos_MenuEvent =============== */ static void Demos_MenuEvent( void *ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_GO: UI_ForceMenuOff (); trap_Cmd_ExecuteText( EXEC_APPEND, va( "demo %s\n", s_demos.list.itemnames[s_demos.list.curvalue]) ); break; case ID_BACK: UI_PopMenu(); break; case ID_SCROLLUP: ScrollList_Key( &s_demos.list, K_UPARROW ); break; case ID_SCROLLDN: ScrollList_Key( &s_demos.list, K_DOWNARROW ); break; } } /* ================= UI_DemosMenu_Key ================= */ static sfxHandle_t UI_DemosMenu_Key( int key ) { menucommon_s *item; item = Menu_ItemAtCursor( &s_demos.menu ); if( key == K_MWHEELUP ) { ScrollList_Key( &s_demos.list, K_UPARROW ); } if( key == K_MWHEELDOWN ) { ScrollList_Key( &s_demos.list, K_DOWNARROW ); } return Menu_DefaultKey( &s_demos.menu, key ); } static void meowdrawdemo( void ) { Menu_Draw(&s_demos.menu); } /* =============== Demos_MenuInit =============== */ static void Demos_MenuInit( void ) { int i; int len; char *demoname, extension[32]; memset( &s_demos, 0 ,sizeof(demos_t) ); s_demos.menu.key = UI_DemosMenu_Key; Demos_Cache(); s_demos.menu.fullscreen = qtrue; s_demos.menu.wrapAround = qtrue; s_demos.menu.draw = meowdrawdemo; s_demos.banner.generic.type = MTYPE_BTEXT; s_demos.banner.generic.x = 320; s_demos.banner.generic.y = 16; s_demos.banner.string = "DEMOS"; s_demos.banner.color = color_white; s_demos.banner.style = UI_CENTER; s_demos.framel.generic.type = MTYPE_BITMAP; s_demos.framel.generic.name = ART_FRAMEL; s_demos.framel.generic.flags = QMF_INACTIVE; s_demos.framel.generic.x = 0; s_demos.framel.generic.y = 78; s_demos.framel.width = 256; s_demos.framel.height = 329; s_demos.framer.generic.type = MTYPE_BITMAP; s_demos.framer.generic.name = ART_FRAMER; s_demos.framer.generic.flags = QMF_INACTIVE; s_demos.framer.generic.x = 376; s_demos.framer.generic.y = 76; s_demos.framer.width = 256; s_demos.framer.height = 334; s_demos.arrows.generic.type = MTYPE_BITMAP; s_demos.arrows.generic.name = ART_ARROWS; s_demos.arrows.generic.flags = QMF_INACTIVE; s_demos.arrows.generic.x = 512+48+12; s_demos.arrows.generic.y = 240-64+48; s_demos.arrows.width = 64; s_demos.arrows.height = 128; s_demos.left.generic.type = MTYPE_BITMAP; s_demos.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_demos.left.generic.x = 512+48+12; s_demos.left.generic.y = 240-64+48; s_demos.left.generic.id = ID_SCROLLUP; s_demos.left.generic.callback = Demos_MenuEvent; s_demos.left.width = 64; s_demos.left.height = 64; s_demos.left.focuspic = ART_ARROWUP; s_demos.right.generic.type = MTYPE_BITMAP; s_demos.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY; s_demos.right.generic.x = 512+48+12; s_demos.right.generic.y = 240+48; s_demos.right.generic.id = ID_SCROLLDN; s_demos.right.generic.callback = Demos_MenuEvent; s_demos.right.width = 64; s_demos.right.height = 64; s_demos.right.focuspic = ART_ARROWDN; s_demos.back.generic.type = MTYPE_BITMAP; s_demos.back.generic.name = ART_BACK0; s_demos.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS; s_demos.back.generic.id = ID_BACK; s_demos.back.generic.callback = Demos_MenuEvent; s_demos.back.generic.x = 0; s_demos.back.generic.y = 480-64; s_demos.back.width = 128; s_demos.back.height = 64; s_demos.back.focuspic = ART_BACK1; s_demos.go.generic.type = MTYPE_BITMAP; s_demos.go.generic.name = ART_GO0; s_demos.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS; s_demos.go.generic.id = ID_GO; s_demos.go.generic.callback = Demos_MenuEvent; s_demos.go.generic.x = 640; s_demos.go.generic.y = 480-64; s_demos.go.width = 128; s_demos.go.height = 64; s_demos.go.focuspic = ART_GO1; s_demos.list.generic.type = MTYPE_SCROLLLIST; s_demos.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS|QMF_SMALLFONT; s_demos.list.generic.callback = Demos_MenuEvent; s_demos.list.generic.id = ID_LIST; s_demos.list.generic.x = 22; s_demos.list.generic.y = 50; s_demos.list.width = 70; s_demos.list.height = 23; Com_sprintf(extension, sizeof(extension), "dm_%d", (int)trap_Cvar_VariableValue( "protocol" ) ); s_demos.list.numitems = trap_FS_GetFileList( "demos", extension, s_demos.names, NAMEBUFSIZE ); s_demos.list.itemnames = (const char **)s_demos.demolist; //s_demos.list.columns = 1; if (!s_demos.list.numitems) { strcpy( s_demos.names, "No Demos Found." ); s_demos.list.numitems = 1; //degenerate case, not selectable s_demos.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); } else if (s_demos.list.numitems > MAX_DEMOS) s_demos.list.numitems = MAX_DEMOS; demoname = s_demos.names; for ( i = 0; i < s_demos.list.numitems; i++ ) { s_demos.list.itemnames[i] = demoname; // strip extension len = strlen( demoname ); if (!Q_stricmp(demoname + len - 4,".dm3")) demoname[len-4] = '\0'; // Q_strupr(demoname); demoname += len + 1; } Menu_AddItem( &s_demos.menu, &s_demos.banner ); Menu_AddItem( &s_demos.menu, &s_demos.framel ); Menu_AddItem( &s_demos.menu, &s_demos.framer ); Menu_AddItem( &s_demos.menu, &s_demos.list ); Menu_AddItem( &s_demos.menu, &s_demos.arrows ); Menu_AddItem( &s_demos.menu, &s_demos.left ); Menu_AddItem( &s_demos.menu, &s_demos.right ); Menu_AddItem( &s_demos.menu, &s_demos.back ); Menu_AddItem( &s_demos.menu, &s_demos.go ); } /* ================= Demos_Cache ================= */ void Demos_Cache( void ) { trap_R_RegisterShaderNoMip( ART_BACK0 ); trap_R_RegisterShaderNoMip( ART_BACK1 ); trap_R_RegisterShaderNoMip( ART_GO0 ); trap_R_RegisterShaderNoMip( ART_GO1 ); trap_R_RegisterShaderNoMip( ART_FRAMEL ); trap_R_RegisterShaderNoMip( ART_FRAMER ); trap_R_RegisterShaderNoMip( ART_ARROWS ); trap_R_RegisterShaderNoMip( ART_ARROWUP ); trap_R_RegisterShaderNoMip( ART_ARROWDN ); } /* =============== UI_DemosMenu =============== */ void UI_DemosMenu( void ) { Demos_MenuInit(); UI_PushMenu( &s_demos.menu ); } openarena_0.8.8.orig/code/q3_ui/ui_sppostgame.c0000644000175000017500000004505411656310261020210 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /* ============================================================================= SINGLE PLAYER POSTGAME MENU ============================================================================= */ #include "ui_local.h" #define MAX_SCOREBOARD_CLIENTS 8 #define AWARD_PRESENTATION_TIME 2000 #define ART_MENU0 "menu/art_blueish/menu_0" #define ART_MENU1 "menu/art_blueish/menu_1" #define ART_REPLAY0 "menu/art_blueish/replay_0" #define ART_REPLAY1 "menu/art_blueish/replay_1" #define ART_NEXT0 "menu/art_blueish/next_0" #define ART_NEXT1 "menu/art_blueish/next_1" #define ID_AGAIN 10 #define ID_NEXT 11 #define ID_MENU 12 typedef struct { menuframework_s menu; menubitmap_s item_again; menubitmap_s item_next; menubitmap_s item_menu; int phase; int ignoreKeysTime; int starttime; int scoreboardtime; int serverId; int clientNums[MAX_SCOREBOARD_CLIENTS]; int ranks[MAX_SCOREBOARD_CLIENTS]; int scores[MAX_SCOREBOARD_CLIENTS]; char placeNames[3][64]; int level; int numClients; int won; int numAwards; int awardsEarned[6]; int awardsLevels[6]; qboolean playedSound[6]; int lastTier; sfxHandle_t winnerSound; } postgameMenuInfo_t; static postgameMenuInfo_t postgameMenuInfo; static char arenainfo[MAX_INFO_VALUE]; char *ui_medalNames[] = {"Accuracy", "Impressive", "Excellent", "Gauntlet", "Frags", "Perfect"}; char *ui_medalPicNames[] = { "menu/medals/medal_accuracy", "menu/medals/medal_impressive", "menu/medals/medal_excellent", "menu/medals/medal_gauntlet", "menu/medals/medal_frags", "menu/medals/medal_victory" }; char *ui_medalSounds[] = { "sound/feedback/accuracy.wav", "sound/feedback/impressive_a.wav", "sound/feedback/excellent_a.wav", "sound/feedback/gauntlet.wav", "sound/feedback/frags.wav", "sound/feedback/perfect.wav" }; /* ================= UI_SPPostgameMenu_AgainEvent ================= */ static void UI_SPPostgameMenu_AgainEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } UI_PopMenu(); trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); } /* ================= UI_SPPostgameMenu_NextEvent ================= */ static void UI_SPPostgameMenu_NextEvent( void* ptr, int event ) { int currentSet; int levelSet; int level; int currentLevel; const char *arenaInfo; if (event != QM_ACTIVATED) { return; } UI_PopMenu(); // handle specially if we just won the training map if( postgameMenuInfo.won == 0 ) { level = 0; } else { level = postgameMenuInfo.level + 1; } levelSet = level / ARENAS_PER_TIER; currentLevel = UI_GetCurrentGame(); if( currentLevel == -1 ) { currentLevel = postgameMenuInfo.level; } currentSet = currentLevel / ARENAS_PER_TIER; if( levelSet > currentSet || levelSet == UI_GetNumSPTiers() ) { level = currentLevel; } arenaInfo = UI_GetArenaInfoByNumber( level ); if ( !arenaInfo ) { return; } UI_SPArena_Start( arenaInfo ); } /* ================= UI_SPPostgameMenu_MenuEvent ================= */ static void UI_SPPostgameMenu_MenuEvent( void* ptr, int event ) { if (event != QM_ACTIVATED) { return; } UI_PopMenu(); trap_Cvar_Set( "nextmap", "" ); trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; levelselect\n" ); } /* ================= UI_SPPostgameMenu_MenuKey ================= */ static sfxHandle_t UI_SPPostgameMenu_MenuKey( int key ) { if ( uis.realtime < postgameMenuInfo.ignoreKeysTime ) { return 0; } if( postgameMenuInfo.phase == 1 ) { trap_Cmd_ExecuteText( EXEC_APPEND, "abort_podium\n" ); postgameMenuInfo.phase = 2; postgameMenuInfo.starttime = uis.realtime; postgameMenuInfo.ignoreKeysTime = uis.realtime + 250; return 0; } if( postgameMenuInfo.phase == 2 ) { postgameMenuInfo.phase = 3; postgameMenuInfo.starttime = uis.realtime; postgameMenuInfo.ignoreKeysTime = uis.realtime + 250; return 0; } if( key == K_ESCAPE || key == K_MOUSE2 ) { return 0; } return Menu_DefaultKey( &postgameMenuInfo.menu, key ); } static int medalLocations[6] = {144, 448, 88, 504, 32, 560}; static void UI_SPPostgameMenu_DrawAwardsMedals( int max ) { int n; int medal; int amount; int x, y; char buf[16]; for( n = 0; n < max; n++ ) { x = medalLocations[n]; y = 64; medal = postgameMenuInfo.awardsEarned[n]; amount = postgameMenuInfo.awardsLevels[n]; UI_DrawNamedPic( x, y, 48, 48, ui_medalPicNames[medal] ); if( medal == AWARD_ACCURACY ) { Com_sprintf( buf, sizeof(buf), "%i%%", amount ); } else { if( amount == 1 ) { continue; } Com_sprintf( buf, sizeof(buf), "%i", amount ); } UI_DrawString( x + 24, y + 52, buf, UI_CENTER, color_yellow ); } } static void UI_SPPostgameMenu_DrawAwardsPresentation( int timer ) { int awardNum; int atimer; vec4_t color; awardNum = timer / AWARD_PRESENTATION_TIME; atimer = timer % AWARD_PRESENTATION_TIME; color[0] = color[1] = color[2] = 1.0f; color[3] = (float)( AWARD_PRESENTATION_TIME - atimer ) / (float)AWARD_PRESENTATION_TIME; UI_DrawProportionalString( 320, 64, ui_medalNames[postgameMenuInfo.awardsEarned[awardNum]], UI_CENTER, color ); UI_SPPostgameMenu_DrawAwardsMedals( awardNum + 1 ); if( !postgameMenuInfo.playedSound[awardNum] ) { postgameMenuInfo.playedSound[awardNum] = qtrue; trap_S_StartLocalSound( trap_S_RegisterSound( ui_medalSounds[postgameMenuInfo.awardsEarned[awardNum]], qfalse ), CHAN_ANNOUNCER ); } } /* ================= UI_SPPostgameMenu_MenuDrawScoreLine ================= */ static void UI_SPPostgameMenu_MenuDrawScoreLine( int n, int y ) { int rank; char name[64]; char info[MAX_INFO_STRING]; if( n > (postgameMenuInfo.numClients + 1) ) { n -= (postgameMenuInfo.numClients + 2); } if( n >= postgameMenuInfo.numClients ) { return; } rank = postgameMenuInfo.ranks[n]; if( rank & RANK_TIED_FLAG ) { UI_DrawString( 640 - 31 * SMALLCHAR_WIDTH, y, "(tie)", UI_LEFT|UI_SMALLFONT, color_white ); rank &= ~RANK_TIED_FLAG; } trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[n], info, MAX_INFO_STRING ); Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) ); Q_CleanStr( name ); UI_DrawString( 640 - 25 * SMALLCHAR_WIDTH, y, va( "#%i: %-16s %2i", rank + 1, name, postgameMenuInfo.scores[n] ), UI_LEFT|UI_SMALLFONT, color_white ); } /* ================= UI_SPPostgameMenu_MenuDraw ================= */ static void UI_SPPostgameMenu_MenuDraw( void ) { int timer; int serverId; int n; char info[MAX_INFO_STRING]; trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) ); serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) ); if( serverId != postgameMenuInfo.serverId ) { UI_PopMenu(); return; } // phase 1 if ( postgameMenuInfo.numClients > 2 ) { UI_DrawProportionalString( 510, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[2], UI_CENTER, color_white ); } UI_DrawProportionalString( 130, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[1], UI_CENTER, color_white ); UI_DrawProportionalString( 320, 480 - 64 - 2 * PROP_HEIGHT, postgameMenuInfo.placeNames[0], UI_CENTER, color_white ); if( postgameMenuInfo.phase == 1 ) { timer = uis.realtime - postgameMenuInfo.starttime; if( timer >= 1000 && postgameMenuInfo.winnerSound ) { trap_S_StartLocalSound( postgameMenuInfo.winnerSound, CHAN_ANNOUNCER ); postgameMenuInfo.winnerSound = 0; } if( timer < 5000 ) { return; } postgameMenuInfo.phase = 2; postgameMenuInfo.starttime = uis.realtime; } // phase 2 if( postgameMenuInfo.phase == 2 ) { timer = uis.realtime - postgameMenuInfo.starttime; if( timer >= ( postgameMenuInfo.numAwards * AWARD_PRESENTATION_TIME ) ) { if( timer < 5000 ) { return; } postgameMenuInfo.phase = 3; postgameMenuInfo.starttime = uis.realtime; } else { UI_SPPostgameMenu_DrawAwardsPresentation( timer ); } } // phase 3 if( postgameMenuInfo.phase == 3 ) { if( uis.demoversion ) { if( postgameMenuInfo.won == 1 && UI_ShowTierVideo( 8 )) { trap_Cvar_Set( "nextmap", "" ); trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic demoEnd.RoQ\n" ); return; } } else if( postgameMenuInfo.won > -1 && UI_ShowTierVideo( postgameMenuInfo.won + 1 )) { if( postgameMenuInfo.won == postgameMenuInfo.lastTier ) { trap_Cvar_Set( "nextmap", "" ); trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic end.RoQ\n" ); return; } trap_Cvar_SetValue( "ui_spSelection", postgameMenuInfo.won * ARENAS_PER_TIER ); trap_Cvar_Set( "nextmap", "levelselect" ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "disconnect; cinematic tier%i.RoQ\n", postgameMenuInfo.won + 1 ) ); return; } postgameMenuInfo.item_again.generic.flags &= ~QMF_INACTIVE; postgameMenuInfo.item_next.generic.flags &= ~QMF_INACTIVE; postgameMenuInfo.item_menu.generic.flags &= ~QMF_INACTIVE; UI_SPPostgameMenu_DrawAwardsMedals( postgameMenuInfo.numAwards ); Menu_Draw( &postgameMenuInfo.menu ); } // draw the scoreboard if( !trap_Cvar_VariableValue( "ui_spScoreboard" ) ) { return; } timer = uis.realtime - postgameMenuInfo.scoreboardtime; if( postgameMenuInfo.numClients <= 3 ) { n = 0; } else { n = timer / 1500 % (postgameMenuInfo.numClients + 2); } UI_SPPostgameMenu_MenuDrawScoreLine( n, 0 ); UI_SPPostgameMenu_MenuDrawScoreLine( n + 1, 0 + SMALLCHAR_HEIGHT ); UI_SPPostgameMenu_MenuDrawScoreLine( n + 2, 0 + 2 * SMALLCHAR_HEIGHT ); } /* ================= UI_SPPostgameMenu_Cache ================= */ void UI_SPPostgameMenu_Cache( void ) { int n; qboolean buildscript; buildscript = trap_Cvar_VariableValue("com_buildscript"); trap_R_RegisterShaderNoMip( ART_MENU0 ); trap_R_RegisterShaderNoMip( ART_MENU1 ); trap_R_RegisterShaderNoMip( ART_REPLAY0 ); trap_R_RegisterShaderNoMip( ART_REPLAY1 ); trap_R_RegisterShaderNoMip( ART_NEXT0 ); trap_R_RegisterShaderNoMip( ART_NEXT1 ); for( n = 0; n < 6; n++ ) { trap_R_RegisterShaderNoMip( ui_medalPicNames[n] ); trap_S_RegisterSound( ui_medalSounds[n], qfalse ); } if( buildscript ) { trap_S_RegisterSound( "music/loss.wav", qfalse ); trap_S_RegisterSound( "music/win.wav", qfalse ); trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse ); } } /* ================= UI_SPPostgameMenu_Init ================= */ static void UI_SPPostgameMenu_Init( void ) { postgameMenuInfo.menu.wrapAround = qtrue; postgameMenuInfo.menu.key = UI_SPPostgameMenu_MenuKey; postgameMenuInfo.menu.draw = UI_SPPostgameMenu_MenuDraw; postgameMenuInfo.ignoreKeysTime = uis.realtime + 1500; UI_SPPostgameMenu_Cache(); postgameMenuInfo.item_menu.generic.type = MTYPE_BITMAP; postgameMenuInfo.item_menu.generic.name = ART_MENU0; postgameMenuInfo.item_menu.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE; postgameMenuInfo.item_menu.generic.x = 0; postgameMenuInfo.item_menu.generic.y = 480-64; postgameMenuInfo.item_menu.generic.callback = UI_SPPostgameMenu_MenuEvent; postgameMenuInfo.item_menu.generic.id = ID_MENU; postgameMenuInfo.item_menu.width = 128; postgameMenuInfo.item_menu.height = 64; postgameMenuInfo.item_menu.focuspic = ART_MENU1; postgameMenuInfo.item_again.generic.type = MTYPE_BITMAP; postgameMenuInfo.item_again.generic.name = ART_REPLAY0; postgameMenuInfo.item_again.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE; postgameMenuInfo.item_again.generic.x = 320; postgameMenuInfo.item_again.generic.y = 480-64; postgameMenuInfo.item_again.generic.callback = UI_SPPostgameMenu_AgainEvent; postgameMenuInfo.item_again.generic.id = ID_AGAIN; postgameMenuInfo.item_again.width = 128; postgameMenuInfo.item_again.height = 64; postgameMenuInfo.item_again.focuspic = ART_REPLAY1; postgameMenuInfo.item_next.generic.type = MTYPE_BITMAP; postgameMenuInfo.item_next.generic.name = ART_NEXT0; postgameMenuInfo.item_next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE; postgameMenuInfo.item_next.generic.x = 640; postgameMenuInfo.item_next.generic.y = 480-64; postgameMenuInfo.item_next.generic.callback = UI_SPPostgameMenu_NextEvent; postgameMenuInfo.item_next.generic.id = ID_NEXT; postgameMenuInfo.item_next.width = 128; postgameMenuInfo.item_next.height = 64; postgameMenuInfo.item_next.focuspic = ART_NEXT1; Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_menu ); Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_again ); Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_next ); } static void Prepname( int index ) { int len; char name[64]; char info[MAX_INFO_STRING]; trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[index], info, MAX_INFO_STRING ); Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) ); Q_CleanStr( name ); len = strlen( name ); while( len && UI_ProportionalStringWidth( name ) > 256 ) { len--; name[len] = 0; } Q_strncpyz( postgameMenuInfo.placeNames[index], name, sizeof(postgameMenuInfo.placeNames[index]) ); } /* ================= UI_SPPostgameMenu_f ================= */ void UI_SPPostgameMenu_f( void ) { int playerGameRank; int playerClientNum; int n; int oldFrags, newFrags; const char *arena; int awardValues[6]; char map[MAX_QPATH]; char info[MAX_INFO_STRING]; memset( &postgameMenuInfo, 0, sizeof(postgameMenuInfo) ); trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) ); postgameMenuInfo.serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) ); trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) ); arena = UI_GetArenaInfoByMap( map ); if ( !arena ) { return; } Q_strncpyz( arenainfo, arena, sizeof(arenainfo) ); postgameMenuInfo.level = atoi( Info_ValueForKey( arenainfo, "num" ) ); postgameMenuInfo.numClients = atoi( UI_Argv( 1 ) ); playerClientNum = atoi( UI_Argv( 2 ) ); playerGameRank = 8; // in case they ended game as a spectator if( postgameMenuInfo.numClients > MAX_SCOREBOARD_CLIENTS ) { postgameMenuInfo.numClients = MAX_SCOREBOARD_CLIENTS; } for( n = 0; n < postgameMenuInfo.numClients; n++ ) { postgameMenuInfo.clientNums[n] = atoi( UI_Argv( 8 + n * 3 + 1 ) ); postgameMenuInfo.ranks[n] = atoi( UI_Argv( 8 + n * 3 + 2 ) ); postgameMenuInfo.scores[n] = atoi( UI_Argv( 8 + n * 3 + 3 ) ); if( postgameMenuInfo.clientNums[n] == playerClientNum ) { playerGameRank = (postgameMenuInfo.ranks[n] & ~RANK_TIED_FLAG) + 1; } } UI_SetBestScore( postgameMenuInfo.level, playerGameRank ); // process award stats and prepare presentation data awardValues[AWARD_ACCURACY] = atoi( UI_Argv( 3 ) ); awardValues[AWARD_IMPRESSIVE] = atoi( UI_Argv( 4 ) ); awardValues[AWARD_EXCELLENT] = atoi( UI_Argv( 5 ) ); awardValues[AWARD_GAUNTLET] = atoi( UI_Argv( 6 ) ); awardValues[AWARD_FRAGS] = atoi( UI_Argv( 7 ) ); awardValues[AWARD_PERFECT] = atoi( UI_Argv( 8 ) ); postgameMenuInfo.numAwards = 0; if( awardValues[AWARD_ACCURACY] >= 50 ) { UI_LogAwardData( AWARD_ACCURACY, 1 ); postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_ACCURACY; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_ACCURACY]; postgameMenuInfo.numAwards++; } if( awardValues[AWARD_IMPRESSIVE] ) { UI_LogAwardData( AWARD_IMPRESSIVE, awardValues[AWARD_IMPRESSIVE] ); postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_IMPRESSIVE; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_IMPRESSIVE]; postgameMenuInfo.numAwards++; } if( awardValues[AWARD_EXCELLENT] ) { UI_LogAwardData( AWARD_EXCELLENT, awardValues[AWARD_EXCELLENT] ); postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_EXCELLENT; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_EXCELLENT]; postgameMenuInfo.numAwards++; } if( awardValues[AWARD_GAUNTLET] ) { UI_LogAwardData( AWARD_GAUNTLET, awardValues[AWARD_GAUNTLET] ); postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_GAUNTLET; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_GAUNTLET]; postgameMenuInfo.numAwards++; } oldFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100; UI_LogAwardData( AWARD_FRAGS, awardValues[AWARD_FRAGS] ); newFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100; if( newFrags > oldFrags ) { postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_FRAGS; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = newFrags * 100; postgameMenuInfo.numAwards++; } if( awardValues[AWARD_PERFECT] ) { UI_LogAwardData( AWARD_PERFECT, 1 ); postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_PERFECT; postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = 1; postgameMenuInfo.numAwards++; } if ( playerGameRank == 1 ) { postgameMenuInfo.won = UI_TierCompleted( postgameMenuInfo.level ); } else { postgameMenuInfo.won = -1; } postgameMenuInfo.starttime = uis.realtime; postgameMenuInfo.scoreboardtime = uis.realtime; trap_Key_SetCatcher( KEYCATCH_UI ); uis.menusp = 0; UI_SPPostgameMenu_Init(); UI_PushMenu( &postgameMenuInfo.menu ); if ( playerGameRank == 1 ) { Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_next ); } else { Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_again ); } Prepname( 0 ); Prepname( 1 ); Prepname( 2 ); if ( playerGameRank != 1 ) { postgameMenuInfo.winnerSound = trap_S_RegisterSound( va( "sound/player/announce/%s_wins.wav", postgameMenuInfo.placeNames[0] ), qfalse ); trap_Cmd_ExecuteText( EXEC_APPEND, "music music/loss\n" ); } else { postgameMenuInfo.winnerSound = trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse ); trap_Cmd_ExecuteText( EXEC_APPEND, "music music/win\n" ); } postgameMenuInfo.phase = 1; postgameMenuInfo.lastTier = UI_GetNumSPTiers(); if ( UI_GetSpecialArenaInfo( "final" ) ) { postgameMenuInfo.lastTier++; } } openarena_0.8.8.orig/code/q3_ui/ui_rankings.c0000644000175000017500000002362411656310261017641 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // ui_rankings.c // #include "ui_local.h" #define RANKINGS_FRAME "menu/art_blueish/cut_frame" #define ID_LOGIN 100 #define ID_LOGOUT 101 #define ID_CREATE 102 #define ID_SPECTATE 103 #define ID_SETUP 104 #define ID_LEAVE 105 typedef struct { menuframework_s menu; menubitmap_s frame; menutext_s login; menutext_s logout; menutext_s create; menutext_s spectate; menutext_s setup; menutext_s leave; } rankings_t; static rankings_t s_rankings; static menuframework_s s_rankings_menu; static menuaction_s s_rankings_login; static menuaction_s s_rankings_logout; static menuaction_s s_rankings_create; static menuaction_s s_rankings_spectate; static menuaction_s s_rankings_setup; static menuaction_s s_rankings_leave; /* =============== Rankings_DrawText =============== */ void Rankings_DrawText( void* self ) { menufield_s *f; qboolean focus; int style; char *txt; char c; float *color; int basex, x, y; f = (menufield_s*)self; basex = f->generic.x; y = f->generic.y + 4; focus = (f->generic.parent->cursor == f->generic.menuPosition); style = UI_LEFT|UI_SMALLFONT; color = text_color_normal; if( focus ) { style |= UI_PULSE; color = text_color_highlight; } // draw the actual text txt = f->field.buffer; color = g_color_table[ColorIndex(COLOR_WHITE)]; x = basex; while ( (c = *txt) != 0 ) { UI_DrawChar( x, y, c, style, color ); txt++; x += SMALLCHAR_WIDTH; } // draw cursor if we have focus if( focus ) { if ( trap_Key_GetOverstrikeMode() ) { c = 11; } else { c = 10; } style &= ~UI_PULSE; style |= UI_BLINK; UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white ); } } /* =============== Rankings_DrawName =============== */ void Rankings_DrawName( void* self ) { menufield_s *f; int length; char* p; f = (menufield_s*)self; // GRANK_FIXME - enforce valid characters for( p = f->field.buffer; *p != '\0'; p++ ) { //if( ispunct(*p) || isspace(*p) ) if( !( ( (*p) >= '0' && (*p) <= '9') || Q_isalpha(*p)) ) { *p = '\0'; } } // strip color codes Q_CleanStr( f->field.buffer ); length = strlen( f->field.buffer ); if( f->field.cursor > length ) { f->field.cursor = length; } Rankings_DrawText( f ); } #if 0 // old version /* =============== Rankings_DrawName =============== */ void Rankings_DrawName( void* self ) { menufield_s* f; int length; f = (menufield_s*)self; // strip color codes Q_CleanStr( f->field.buffer ); length = strlen( f->field.buffer ); if( f->field.cursor > length ) { f->field.cursor = length; } // show beginning of long names /* if( Menu_ItemAtCursor( f->generic.parent ) != f ) { if( f->field.scroll > 0 ) { f->field.cursor = 0; f->field.scroll = 0; } } */ MenuField_Draw( f ); } #endif /* =============== Rankings_DrawPassword =============== */ void Rankings_DrawPassword( void* self ) { menufield_s* f; char password[MAX_EDIT_LINE]; int length; int i; char* p; f = (menufield_s*)self; // GRANK_FIXME - enforce valid characters for( p = f->field.buffer; *p != '\0'; p++ ) { //if( ispunct(*p) || isspace(*p) ) if( !( ( (*p) >= '0' && (*p) <= '9') || Q_isalpha(*p)) ) { *p = '\0'; } } length = strlen( f->field.buffer ); if( f->field.cursor > length ) { f->field.cursor = length; } // save password Q_strncpyz( password, f->field.buffer, sizeof(password) ); // mask password with * for( i = 0; i < length; i++ ) { f->field.buffer[i] = '*'; } // draw masked password Rankings_DrawText( f ); //MenuField_Draw( f ); // restore password Q_strncpyz( f->field.buffer, password, sizeof(f->field.buffer) ); } /* =============== Rankings_MenuEvent =============== */ static void Rankings_MenuEvent( void* ptr, int event ) { if( event != QM_ACTIVATED ) { return; } switch( ((menucommon_s*)ptr)->id ) { case ID_LOGIN: UI_LoginMenu(); break; case ID_LOGOUT: // server side masqueraded player logout first trap_CL_UI_RankUserRequestLogout(); UI_ForceMenuOff(); break; case ID_CREATE: UI_SignupMenu(); break; case ID_SPECTATE: trap_Cmd_ExecuteText( EXEC_APPEND, "cmd rank_spectate\n" ); UI_ForceMenuOff(); break; case ID_SETUP: UI_SetupMenu(); break; case ID_LEAVE: trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" ); UI_ForceMenuOff(); break; } } /* =============== Rankings_MenuInit =============== */ void Rankings_MenuInit( void ) { grank_status_t status; int y; memset( &s_rankings, 0, sizeof(s_rankings) ); Rankings_Cache(); s_rankings.menu.wrapAround = qtrue; s_rankings.menu.fullscreen = qfalse; s_rankings.frame.generic.type = MTYPE_BITMAP; s_rankings.frame.generic.flags = QMF_INACTIVE; s_rankings.frame.generic.name = RANKINGS_FRAME; s_rankings.frame.generic.x = 142; s_rankings.frame.generic.y = 118; s_rankings.frame.width = 359; s_rankings.frame.height = 256; y = 194; s_rankings.login.generic.type = MTYPE_PTEXT; s_rankings.login.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.login.generic.id = ID_LOGIN; s_rankings.login.generic.callback = Rankings_MenuEvent; s_rankings.login.generic.x = 320; s_rankings.login.generic.y = y; s_rankings.login.string = "LOGIN"; s_rankings.login.style = UI_CENTER|UI_SMALLFONT; s_rankings.login.color = colorRed; y += 20; s_rankings.logout.generic.type = MTYPE_PTEXT; s_rankings.logout.generic.flags = QMF_HIDDEN|QMF_INACTIVE|QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.logout.generic.id = ID_LOGOUT; s_rankings.logout.generic.callback = Rankings_MenuEvent; s_rankings.logout.generic.x = 320; s_rankings.logout.generic.y = y; s_rankings.logout.string = "LOGOUT"; s_rankings.logout.style = UI_CENTER|UI_SMALLFONT; s_rankings.logout.color = colorRed; s_rankings.create.generic.type = MTYPE_PTEXT; s_rankings.create.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.create.generic.id = ID_CREATE; s_rankings.create.generic.callback = Rankings_MenuEvent; s_rankings.create.generic.x = 320; s_rankings.create.generic.y = y; s_rankings.create.string = "SIGN UP"; s_rankings.create.style = UI_CENTER|UI_SMALLFONT; s_rankings.create.color = colorRed; y += 20; s_rankings.spectate.generic.type = MTYPE_PTEXT; s_rankings.spectate.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.spectate.generic.id = ID_SPECTATE; s_rankings.spectate.generic.callback = Rankings_MenuEvent; s_rankings.spectate.generic.x = 320; s_rankings.spectate.generic.y = y; s_rankings.spectate.string = "SPECTATE"; s_rankings.spectate.style = UI_CENTER|UI_SMALLFONT; s_rankings.spectate.color = colorRed; y += 20; s_rankings.setup.generic.type = MTYPE_PTEXT; s_rankings.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.setup.generic.id = ID_SETUP; s_rankings.setup.generic.callback = Rankings_MenuEvent; s_rankings.setup.generic.x = 320; s_rankings.setup.generic.y = y; s_rankings.setup.string = "SETUP"; s_rankings.setup.style = UI_CENTER|UI_SMALLFONT; s_rankings.setup.color = colorRed; y += 20; s_rankings.leave.generic.type = MTYPE_PTEXT; s_rankings.leave.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS; s_rankings.leave.generic.id = ID_LEAVE; s_rankings.leave.generic.callback = Rankings_MenuEvent; s_rankings.leave.generic.x = 320; s_rankings.leave.generic.y = y; s_rankings.leave.string = "LEAVE ARENA"; s_rankings.leave.style = UI_CENTER|UI_SMALLFONT; s_rankings.leave.color = colorRed; y += 20; status = (grank_status_t)trap_Cvar_VariableValue("client_status"); if( (status != QGR_STATUS_NEW) && (status != QGR_STATUS_SPECTATOR) ) { s_rankings.login.generic.flags |= QMF_HIDDEN | QMF_INACTIVE; s_rankings.create.generic.flags |= QMF_HIDDEN | QMF_INACTIVE; s_rankings.spectate.generic.flags |= QMF_HIDDEN | QMF_INACTIVE; s_rankings.logout.generic.flags &= ~(QMF_HIDDEN | QMF_INACTIVE); } if ( (status == QGR_STATUS_VALIDATING) || (status == QGR_STATUS_PENDING) || (status == QGR_STATUS_LEAVING) ) { s_rankings.login.generic.flags |= QMF_GRAYED; s_rankings.create.generic.flags |= QMF_GRAYED; s_rankings.logout.generic.flags |= QMF_GRAYED; } //GRank FIXME -- don't need setup option any more s_rankings.setup.generic.flags |= QMF_HIDDEN | QMF_INACTIVE; Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.frame ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.login ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.logout ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.create ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.spectate ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.setup ); Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.leave ); } /* =============== Rankings_Cache =============== */ void Rankings_Cache( void ) { trap_R_RegisterShaderNoMip( RANKINGS_FRAME ); } /* =============== UI_RankingsMenu =============== */ void UI_RankingsMenu( void ) { Rankings_MenuInit(); UI_PushMenu ( &s_rankings.menu ); } openarena_0.8.8.orig/code/qcommon/0000755000175000017500000000000011720470203015601 5ustar smcvsmcvopenarena_0.8.8.orig/code/qcommon/surfaceflags.h0000644000175000017500000000676111656310263020440 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // This file must be identical in the quake and utils directories // contents flags are seperate bits // a given brush can contribute multiple content bits // these definitions also need to be in q_shared.h! #define CONTENTS_SOLID 1 // an eye is never valid in a solid #define CONTENTS_LAVA 8 #define CONTENTS_SLIME 16 #define CONTENTS_WATER 32 #define CONTENTS_FOG 64 #define CONTENTS_NOTTEAM1 0x0080 #define CONTENTS_NOTTEAM2 0x0100 #define CONTENTS_NOBOTCLIP 0x0200 #define CONTENTS_AREAPORTAL 0x8000 #define CONTENTS_PLAYERCLIP 0x10000 #define CONTENTS_MONSTERCLIP 0x20000 //bot specific contents types #define CONTENTS_TELEPORTER 0x40000 #define CONTENTS_JUMPPAD 0x80000 #define CONTENTS_CLUSTERPORTAL 0x100000 #define CONTENTS_DONOTENTER 0x200000 #define CONTENTS_BOTCLIP 0x400000 #define CONTENTS_MOVER 0x800000 #define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity #define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game #define CONTENTS_CORPSE 0x4000000 #define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp #define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp #define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside #define CONTENTS_TRIGGER 0x40000000 #define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava) #define SURF_NODAMAGE 0x1 // never give falling damage #define SURF_SLICK 0x2 // effects game physics #define SURF_SKY 0x4 // lighting from environment map #define SURF_LADDER 0x8 #define SURF_NOIMPACT 0x10 // don't make missile explosions #define SURF_NOMARKS 0x20 // don't leave missile marks #define SURF_FLESH 0x40 // make flesh sounds and effects #define SURF_NODRAW 0x80 // don't generate a drawsurface at all #define SURF_HINT 0x100 // make a primary bsp splitter #define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes #define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap #define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes #define SURF_METALSTEPS 0x1000 // clanking footsteps #define SURF_NOSTEPS 0x2000 // no footstep sounds #define SURF_NONSOLID 0x4000 // don't collide against curves with this set #define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light #define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map #define SURF_NODLIGHT 0x20000 // don't dlight even if solid (solid lava, skies) #define SURF_DUST 0x40000 // leave a dust trail when walking on this surface openarena_0.8.8.orig/code/qcommon/q_shared.c0000644000175000017500000006041711656310263017552 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // q_shared.c -- stateless support routines that are included in each code dll #include "q_shared.h" float Com_Clamp( float min, float max, float value ) { if ( value < min ) { return min; } if ( value > max ) { return max; } return value; } /* ============ COM_SkipPath ============ */ char *COM_SkipPath (char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname=='/') last = pathname+1; pathname++; } return last; } /* ============ COM_GetExtension ============ */ const char *COM_GetExtension( const char *name ) { int length, i; length = strlen(name)-1; i = length; while (name[i] != '.') { i--; if (name[i] == '/' || i == 0) return ""; // no extension } return &name[i+1]; } /* ============ COM_StripExtension ============ */ void COM_StripExtension( const char *in, char *out, int destsize ) { int length; Q_strncpyz(out, in, destsize); length = strlen(out)-1; while (length > 0 && out[length] != '.') { length--; if (out[length] == '/') return; // no extension } if (length) out[length] = 0; } /* ================== COM_DefaultExtension ================== */ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) { char oldPath[MAX_QPATH]; char *src; // // if path doesn't have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; while (*src != '/' && src != path) { if ( *src == '.' ) { return; // it has an extension } src--; } Q_strncpyz( oldPath, path, sizeof( oldPath ) ); Com_sprintf( path, maxSize, "%s%s", oldPath, extension ); } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ /* // can't just use function pointers, or dll linkage can // mess up when qcommon is included in multiple places static short (*_BigShort) (short l); static short (*_LittleShort) (short l); static int (*_BigLong) (int l); static int (*_LittleLong) (int l); static qint64 (*_BigLong64) (qint64 l); static qint64 (*_LittleLong64) (qint64 l); static float (*_BigFloat) (const float *l); static float (*_LittleFloat) (const float *l); short BigShort(short l){return _BigShort(l);} short LittleShort(short l) {return _LittleShort(l);} int BigLong (int l) {return _BigLong(l);} int LittleLong (int l) {return _LittleLong(l);} qint64 BigLong64 (qint64 l) {return _BigLong64(l);} qint64 LittleLong64 (qint64 l) {return _LittleLong64(l);} float BigFloat (const float *l) {return _BigFloat(l);} float LittleFloat (const float *l) {return _LittleFloat(l);} */ short ShortSwap (short l) { byte b1,b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } short ShortNoSwap (short l) { return l; } int LongSwap (int l) { byte b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } int LongNoSwap (int l) { return l; } qint64 Long64Swap (qint64 ll) { qint64 result; result.b0 = ll.b7; result.b1 = ll.b6; result.b2 = ll.b5; result.b3 = ll.b4; result.b4 = ll.b3; result.b5 = ll.b2; result.b6 = ll.b1; result.b7 = ll.b0; return result; } qint64 Long64NoSwap (qint64 ll) { return ll; } typedef union { float f; unsigned int i; } _FloatByteUnion; float FloatSwap (const float *f) { _FloatByteUnion out; out.f = *f; out.i = LongSwap(out.i); return out.f; } float FloatNoSwap (const float *f) { return *f; } /* ================ Swap_Init ================ */ /* void Swap_Init (void) { byte swaptest[2] = {1,0}; // set the byte swapping variables in a portable manner if ( *(short *)swaptest == 1) { _BigShort = ShortSwap; _LittleShort = ShortNoSwap; _BigLong = LongSwap; _LittleLong = LongNoSwap; _BigLong64 = Long64Swap; _LittleLong64 = Long64NoSwap; _BigFloat = FloatSwap; _LittleFloat = FloatNoSwap; } else { _BigShort = ShortNoSwap; _LittleShort = ShortSwap; _BigLong = LongNoSwap; _LittleLong = LongSwap; _BigLong64 = Long64NoSwap; _LittleLong64 = Long64Swap; _BigFloat = FloatNoSwap; _LittleFloat = FloatSwap; } } */ /* ============================================================================ PARSING ============================================================================ */ static char com_token[MAX_TOKEN_CHARS]; static char com_parsename[MAX_TOKEN_CHARS]; static int com_lines; void COM_BeginParseSession( const char *name ) { com_lines = 0; Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name); } int COM_GetCurrentParseLine( void ) { return com_lines; } char *COM_Parse( char **data_p ) { return COM_ParseExt( data_p, qtrue ); } void COM_ParseError( char *format, ... ) { va_list argptr; static char string[4096]; va_start (argptr, format); Q_vsnprintf (string, sizeof(string), format, argptr); va_end (argptr); Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, com_lines, string); } void COM_ParseWarning( char *format, ... ) { va_list argptr; static char string[4096]; va_start (argptr, format); Q_vsnprintf (string, sizeof(string), format, argptr); va_end (argptr); Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, com_lines, string); } /* ============== COM_Parse Parse a token out of a string Will never return NULL, just empty strings If "allowLineBreaks" is qtrue then an empty string will be returned if the next token is a newline. ============== */ static char *SkipWhitespace( char *data, qboolean *hasNewLines ) { int c; while( (c = *data) <= ' ') { if( !c ) { return NULL; } if( c == '\n' ) { com_lines++; *hasNewLines = qtrue; } data++; } return data; } int COM_Compress( char *data_p ) { char *in, *out; int c; qboolean newline = qfalse, whitespace = qfalse; in = out = data_p; if (in) { while ((c = *in) != 0) { // skip double slash comments if ( c == '/' && in[1] == '/' ) { while (*in && *in != '\n') { in++; } // skip /* */ comments } else if ( c == '/' && in[1] == '*' ) { while ( *in && ( *in != '*' || in[1] != '/' ) ) in++; if ( *in ) in += 2; // record when we hit a newline } else if ( c == '\n' || c == '\r' ) { newline = qtrue; in++; // record when we hit whitespace } else if ( c == ' ' || c == '\t') { whitespace = qtrue; in++; // an actual token } else { // if we have a pending newline, emit it (and it counts as whitespace) if (newline) { *out++ = '\n'; newline = qfalse; whitespace = qfalse; } if (whitespace) { *out++ = ' '; whitespace = qfalse; } // copy quoted strings unmolested if (c == '"') { *out++ = c; in++; while (1) { c = *in; if (c && c != '"') { *out++ = c; in++; } else { break; } } if (c == '"') { *out++ = c; in++; } } else { *out = c; out++; in++; } } } *out = 0; return out - data_p; } return 0; } char *COM_ParseExt( char **data_p, qboolean allowLineBreaks ) { int c = 0, len; qboolean hasNewLines = qfalse; char *data; data = *data_p; len = 0; com_token[0] = 0; // make sure incoming data is valid if ( !data ) { *data_p = NULL; return com_token; } while ( 1 ) { // skip whitespace data = SkipWhitespace( data, &hasNewLines ); if ( !data ) { *data_p = NULL; return com_token; } if ( hasNewLines && !allowLineBreaks ) { *data_p = data; return com_token; } c = *data; // skip double slash comments if ( c == '/' && data[1] == '/' ) { data += 2; while (*data && *data != '\n') { data++; } } // skip /* */ comments else if ( c=='/' && data[1] == '*' ) { data += 2; while ( *data && ( *data != '*' || data[1] != '/' ) ) { data++; } if ( *data ) { data += 2; } } else { break; } } // handle quoted strings if (c == '\"') { data++; while (1) { c = *data++; if (c=='\"' || !c) { com_token[len] = 0; *data_p = ( char * ) data; return com_token; } if (len < MAX_TOKEN_CHARS - 1) { com_token[len] = c; len++; } } } // parse a regular word do { if (len < MAX_TOKEN_CHARS - 1) { com_token[len] = c; len++; } data++; c = *data; if ( c == '\n' ) com_lines++; } while (c>32); com_token[len] = 0; *data_p = ( char * ) data; return com_token; } #if 0 // no longer used /* =============== COM_ParseInfos =============== */ int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } infos[count][0] = 0; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "" ); } Info_SetValueForKey( infos[count], key, token ); } count++; } return count; } #endif /* ================== COM_MatchToken ================== */ void COM_MatchToken( char **buf_p, char *match ) { char *token; token = COM_Parse( buf_p ); if ( strcmp( token, match ) ) { Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match ); } } /* ================= SkipBracedSection The next token should be an open brace. Skips until a matching close brace is found. Internal brace depths are properly skipped. ================= */ void SkipBracedSection (char **program) { char *token; int depth; depth = 0; do { token = COM_ParseExt( program, qtrue ); if( token[1] == 0 ) { if( token[0] == '{' ) { depth++; } else if( token[0] == '}' ) { depth--; } } } while( depth && *program ); } /* ================= SkipRestOfLine ================= */ void SkipRestOfLine ( char **data ) { char *p; int c; p = *data; while ( (c = *p++) != 0 ) { if ( c == '\n' ) { com_lines++; break; } } *data = p; } void Parse1DMatrix (char **buf_p, int x, float *m) { char *token; int i; COM_MatchToken( buf_p, "(" ); for (i = 0 ; i < x ; i++) { token = COM_Parse(buf_p); m[i] = atof(token); } COM_MatchToken( buf_p, ")" ); } void Parse2DMatrix (char **buf_p, int y, int x, float *m) { int i; COM_MatchToken( buf_p, "(" ); for (i = 0 ; i < y ; i++) { Parse1DMatrix (buf_p, x, m + i * x); } COM_MatchToken( buf_p, ")" ); } void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m) { int i; COM_MatchToken( buf_p, "(" ); for (i = 0 ; i < z ; i++) { Parse2DMatrix (buf_p, y, x, m + i * x*y); } COM_MatchToken( buf_p, ")" ); } /* ============================================================================ LIBRARY REPLACEMENT FUNCTIONS ============================================================================ */ int Q_isprint( int c ) { if ( c >= 0x20 && c <= 0x7E ) return ( 1 ); return ( 0 ); } int Q_islower( int c ) { if (c >= 'a' && c <= 'z') return ( 1 ); return ( 0 ); } int Q_isupper( int c ) { if (c >= 'A' && c <= 'Z') return ( 1 ); return ( 0 ); } int Q_isalpha( int c ) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return ( 1 ); return ( 0 ); } char* Q_strrchr( const char* string, int c ) { char cc = c; char *s; char *sp=(char *)0; s = (char*)string; while (*s) { if (*s == cc) sp = s; s++; } if (cc == 0) sp = s; return sp; } /* ============= Q_strncpyz Safe strncpy that ensures a trailing zero ============= */ void Q_strncpyz( char *dest, const char *src, int destsize ) { if ( !dest ) { Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" ); } if ( !src ) { Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" ); } if ( destsize < 1 ) { Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); } strncpy( dest, src, destsize-1 ); dest[destsize-1] = 0; } int Q_stricmpn (const char *s1, const char *s2, int n) { int c1, c2; if ( s1 == NULL ) { if ( s2 == NULL ) return 0; else return -1; } else if ( s2==NULL ) return 1; do { c1 = *s1++; c2 = *s2++; if (!n--) { return 0; // strings are equal until end point } if (c1 != c2) { if (c1 >= 'a' && c1 <= 'z') { c1 -= ('a' - 'A'); } if (c2 >= 'a' && c2 <= 'z') { c2 -= ('a' - 'A'); } if (c1 != c2) { return c1 < c2 ? -1 : 1; } } } while (c1); return 0; // strings are equal } int Q_strncmp (const char *s1, const char *s2, int n) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (!n--) { return 0; // strings are equal until end point } if (c1 != c2) { return c1 < c2 ? -1 : 1; } } while (c1); return 0; // strings are equal } int Q_stricmp (const char *s1, const char *s2) { return (s1 && s2) ? Q_stricmpn (s1, s2, 99999) : -1; } char *Q_strlwr( char *s1 ) { char *s; s = s1; while ( *s ) { *s = tolower(*s); s++; } return s1; } char *Q_strupr( char *s1 ) { char *s; s = s1; while ( *s ) { *s = toupper(*s); s++; } return s1; } // never goes past bounds or leaves without a terminating 0 void Q_strcat( char *dest, int size, const char *src ) { int l1; l1 = strlen( dest ); if ( l1 >= size ) { Com_Error( ERR_FATAL, "Q_strcat: already overflowed" ); } Q_strncpyz( dest + l1, src, size - l1 ); } /* * Find the first occurrence of find in s. */ const char *Q_stristr( const char *s, const char *find) { char c, sc; size_t len; if ((c = *find++) != 0) { if (c >= 'a' && c <= 'z') { c -= ('a' - 'A'); } len = strlen(find); do { do { if ((sc = *s++) == 0) return NULL; if (sc >= 'a' && sc <= 'z') { sc -= ('a' - 'A'); } } while (sc != c); } while (Q_stricmpn(s, find, len) != 0); s--; } return s; } int Q_PrintStrlen( const char *string ) { int len; const char *p; if( !string ) { return 0; } len = 0; p = string; while( *p ) { if( Q_IsColorString( p ) ) { p += 2; continue; } p++; len++; } return len; } char *Q_CleanStr( char *string ) { char* d; char* s; int c; qboolean hadColor = qfalse; s = string; d = string; while ((c = *s) != 0 ) { if ( Q_IsColorString( s ) ) { s++; hadColor = qtrue; } else if ( c >= 0x20 && c <= 0x7E ) { *d++ = c; } s++; } *d = '\0'; if(hadColor) return Q_CleanStr( string ); else return string; } int Q_CountChar(const char *string, char tocount) { int count; for(count = 0; *string; string++) { if(*string == tocount) count++; } return count; } void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { int len; va_list argptr; char bigbuffer[32000]; // big, but small enough to fit in PPC stack va_start (argptr,fmt); len = Q_vsnprintf (bigbuffer, sizeof(bigbuffer), fmt,argptr); va_end (argptr); if ( len >= sizeof( bigbuffer ) ) { Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" ); } if (len >= size) { Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size); #ifdef _DEBUG __asm { int 3; } #endif } Q_strncpyz (dest, bigbuffer, size ); } /* ============ va does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functions. ============ */ char * QDECL va( char *format, ... ) { va_list argptr; static char string[2][32000]; // in case va is called by nested functions static int index = 0; char *buf; buf = string[index & 1]; index++; va_start (argptr, format); Q_vsnprintf (buf, sizeof(*string), format, argptr); va_end (argptr); return buf; } /* ============ Com_TruncateLongString Assumes buffer is atleast TRUNCATE_LENGTH big ============ */ void Com_TruncateLongString( char *buffer, const char *s ) { int length = strlen( s ); if( length <= TRUNCATE_LENGTH ) Q_strncpyz( buffer, s, TRUNCATE_LENGTH ); else { Q_strncpyz( buffer, s, ( TRUNCATE_LENGTH / 2 ) - 3 ); Q_strcat( buffer, TRUNCATE_LENGTH, " ... " ); Q_strcat( buffer, TRUNCATE_LENGTH, s + length - ( TRUNCATE_LENGTH / 2 ) + 3 ); } } /* ===================================================================== INFO STRINGS ===================================================================== */ /* =============== Info_ValueForKey Searches the string for the given key and returns the associated value, or an empty string. FIXME: overflow check? =============== */ char *Info_ValueForKey( const char *s, const char *key ) { char pkey[BIG_INFO_KEY]; static char value[2][BIG_INFO_VALUE]; // use two buffers so compares // work without stomping on each other static int valueindex = 0; char *o; if ( !s || !key ) { return ""; } if ( strlen( s ) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" ); } valueindex ^= 1; if (*s == '\\') s++; while (1) { o = pkey; while (*s != '\\') { if (!*s) return ""; *o++ = *s++; } *o = 0; s++; o = value[valueindex]; while (*s != '\\' && *s) { *o++ = *s++; } *o = 0; if (!Q_stricmp (key, pkey) ) return value[valueindex]; if (!*s) break; s++; } return ""; } /* =================== Info_NextPair Used to itterate through all the key/value pairs in an info string =================== */ void Info_NextPair( const char **head, char *key, char *value ) { char *o; const char *s; s = *head; if ( *s == '\\' ) { s++; } key[0] = 0; value[0] = 0; o = key; while ( *s != '\\' ) { if ( !*s ) { *o = 0; *head = s; return; } *o++ = *s++; } *o = 0; s++; o = value; while ( *s != '\\' && *s ) { *o++ = *s++; } *o = 0; *head = s; } /* =================== Info_RemoveKey =================== */ void Info_RemoveKey( char *s, const char *key ) { char *start; char pkey[MAX_INFO_KEY]; char value[MAX_INFO_VALUE]; char *o; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" ); } if (strchr (key, '\\')) { return; } while (1) { start = s; if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) { memmove(start, s, strlen(s) + 1); // remove this part return; } if (!*s) return; } } /* =================== Info_RemoveKey_Big =================== */ void Info_RemoveKey_Big( char *s, const char *key ) { char *start; char pkey[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; char *o; if ( strlen( s ) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" ); } if (strchr (key, '\\')) { return; } while (1) { start = s; if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) { strcpy (start, s); // remove this part return; } if (!*s) return; } } /* ================== Info_Validate Some characters are illegal in info strings because they can mess up the server's parsing ================== */ qboolean Info_Validate( const char *s ) { if ( strchr( s, '\"' ) ) { return qfalse; } if ( strchr( s, ';' ) ) { return qfalse; } return qtrue; } /* ================== Info_SetValueForKey Changes or adds a key/value pair ================== */ void Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING]; const char* blacklist = "\\;\""; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" ); } for(; *blacklist; ++blacklist) { if (strchr (key, *blacklist) || strchr (value, *blacklist)) { Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value); return; } } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) >= MAX_INFO_STRING) { Com_Printf ("Info string length exceeded\n"); return; } strcat (newi, s); strcpy (s, newi); } /* ================== Info_SetValueForKey_Big Changes or adds a key/value pair ================== */ void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) { char newi[BIG_INFO_STRING]; const char* blacklist = "\\;\""; if ( strlen( s ) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" ); } for(; *blacklist; ++blacklist) { if (strchr (key, *blacklist) || strchr (value, *blacklist)) { Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value); return; } } Info_RemoveKey_Big (s, key); if (!value || !strlen(value)) return; Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) >= BIG_INFO_STRING) { Com_Printf ("BIG Info string length exceeded\n"); return; } strcat (s, newi); } //==================================================================== /* ================== Com_CharIsOneOfCharset ================== */ static qboolean Com_CharIsOneOfCharset( char c, char *set ) { int i; for( i = 0; i < strlen( set ); i++ ) { if( set[ i ] == c ) return qtrue; } return qfalse; } /* ================== Com_SkipCharset ================== */ char *Com_SkipCharset( char *s, char *sep ) { char *p = s; while( p ) { if( Com_CharIsOneOfCharset( *p, sep ) ) p++; else break; } return p; } /* ================== Com_SkipTokens ================== */ char *Com_SkipTokens( char *s, int numTokens, char *sep ) { int sepCount = 0; char *p = s; while( sepCount < numTokens ) { if( Com_CharIsOneOfCharset( *p++, sep ) ) { sepCount++; while( Com_CharIsOneOfCharset( *p, sep ) ) p++; } else if( *p == '\0' ) break; } if( sepCount == numTokens ) return p; else return s; } openarena_0.8.8.orig/code/qcommon/q_shared.h0000644000175000017500000011313411656310263017552 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifndef __Q_SHARED_H #define __Q_SHARED_H // q_shared.h -- included first by ALL program modules. // A user mod should never modify this file //#ifdef STANDALONE #define PRODUCT_NAME "ioq3+oa" #define BASEGAME "baseoa" #define CLIENT_WINDOW_TITLE "OpenArena" #define CLIENT_WINDOW_MIN_TITLE "OA" /*#else #define PRODUCT_NAME "ioq3" #define BASEGAME "baseq3" #define CLIENT_WINDOW_TITLE "ioquake3" #define CLIENT_WINDOW_MIN_TITLE "ioq3" #endif*/ #ifdef _MSC_VER #define PRODUCT_VERSION "1.35" #endif #define Q3_VERSION PRODUCT_NAME " " PRODUCT_VERSION #define MAX_TEAMNAME 32 #ifdef _MSC_VER #pragma warning(disable : 4018) // signed/unsigned mismatch #pragma warning(disable : 4032) #pragma warning(disable : 4051) #pragma warning(disable : 4057) // slightly different base types #pragma warning(disable : 4100) // unreferenced formal parameter #pragma warning(disable : 4115) #pragma warning(disable : 4125) // decimal digit terminates octal escape sequence #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4136) #pragma warning(disable : 4152) // nonstandard extension, function/data pointer conversion in expression //#pragma warning(disable : 4201) //#pragma warning(disable : 4214) #pragma warning(disable : 4244) #pragma warning(disable : 4142) // benign redefinition //#pragma warning(disable : 4305) // truncation from const double to float //#pragma warning(disable : 4310) // cast truncates constant value //#pragma warning(disable: 4505) // unreferenced local function has been removed #pragma warning(disable : 4514) #pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4711) // selected for automatic inline expansion #pragma warning(disable : 4220) // varargs matches remaining parameters //#pragma intrinsic( memset, memcpy ) #endif //Ignore __attribute__ on non-gcc platforms #ifndef __GNUC__ #ifndef __attribute__ #define __attribute__(x) #endif #endif #if (defined _MSC_VER) #define Q_EXPORT __declspec(dllexport) #elif (defined __SUNPRO_C) #define Q_EXPORT __global #elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun)) #define Q_EXPORT __attribute__((visibility("default"))) #else #define Q_EXPORT #endif /********************************************************************** VM Considerations The VM can not use the standard system headers because we aren't really using the compiler they were meant for. We use bg_lib.h which contains prototypes for the functions we define for our own use in bg_lib.c. When writing mods, please add needed headers HERE, do not start including stuff like in the various .c files that make up each of the VMs since you will be including system headers files can will have issues. Remember, if you use a C library function that is not defined in bg_lib.c, you will have to add your own version for support in the VM. **********************************************************************/ #ifdef Q3_VM #include "../game/bg_lib.h" typedef int intptr_t; #else #include #include #include #include #include #include #include #include #include // vsnprintf is ISO/IEC 9899:1999 // abstracting this to make it portable #ifdef _WIN32 #define Q_vsnprintf _vsnprintf #define Q_snprintf _snprintf #else #define Q_vsnprintf vsnprintf #define Q_snprintf snprintf #endif #ifdef _MSC_VER #include typedef __int64 int64_t; typedef __int32 int32_t; typedef __int16 int16_t; typedef __int8 int8_t; typedef unsigned __int64 uint64_t; typedef unsigned __int32 uint32_t; typedef unsigned __int16 uint16_t; typedef unsigned __int8 uint8_t; #else #include #endif #endif #include "q_platform.h" //============================================================= typedef unsigned char byte; typedef enum {qfalse, qtrue} qboolean; typedef union { float f; int i; unsigned int ui; } floatint_t; typedef int qhandle_t; typedef int sfxHandle_t; typedef int fileHandle_t; typedef int clipHandle_t; #define PAD(x,y) (((x)+(y)-1) & ~((y)-1)) #ifdef __GNUC__ #define ALIGN(x) __attribute__((aligned(x))) #else #define ALIGN(x) #endif #ifndef NULL #define NULL ((void *)0) #endif #define MAX_QINT 0x7fffffff #define MIN_QINT (-MAX_QINT-1) // angle indexes #define PITCH 0 // up / down #define YAW 1 // left / right #define ROLL 2 // fall over // the game guarantees that no string from the network will ever // exceed MAX_STRING_CHARS #define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString #define MAX_STRING_TOKENS 1024 // max tokens resulting from Cmd_TokenizeString #define MAX_TOKEN_CHARS 1024 // max length of an individual token #define MAX_INFO_STRING 1024 #define MAX_INFO_KEY 1024 #define MAX_INFO_VALUE 1024 #define BIG_INFO_STRING 8192 // used for system info key only #define BIG_INFO_KEY 8192 #define BIG_INFO_VALUE 8192 #define MAX_QPATH 64 // max length of a quake game pathname #ifdef PATH_MAX #define MAX_OSPATH PATH_MAX #else #define MAX_OSPATH 256 // max length of a filesystem pathname #endif #define MAX_NAME_LENGTH 32 // max length of a client name #define MAX_SAY_TEXT 150 // paramters for command buffer stuffing typedef enum { EXEC_NOW, // don't return until completed, a VM should NEVER use this, // because some commands might cause the VM to be unloaded... EXEC_INSERT, // insert at current position, but don't run yet EXEC_APPEND // add to end of the command buffer (normal case) } cbufExec_t; // // these aren't needed by any of the VMs. put in another header? // #define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility // print levels from renderer (FIXME: set up for game / cgame?) typedef enum { PRINT_ALL, PRINT_DEVELOPER, // only print when "developer 1" PRINT_WARNING, PRINT_ERROR } printParm_t; #ifdef ERR_FATAL #undef ERR_FATAL // this is be defined in malloc.h #endif // parameters to the main Error routine typedef enum { ERR_FATAL, // exit the entire game with a popup window ERR_DROP, // print to console and disconnect from game ERR_SERVERDISCONNECT, // don't kill server ERR_DISCONNECT, // client disconnected from the server ERR_NEED_CD // pop up the need-cd dialog } errorParm_t; // font rendering values used by ui and cgame #define PROP_GAP_WIDTH 3 #define PROP_SPACE_WIDTH 8 #define PROP_HEIGHT 27 #define PROP_SMALL_SIZE_SCALE 0.75 #define BLINK_DIVISOR 200 #define PULSE_DIVISOR 75 #define UI_LEFT 0x00000000 // default #define UI_CENTER 0x00000001 #define UI_RIGHT 0x00000002 #define UI_FORMATMASK 0x00000007 #define UI_SMALLFONT 0x00000010 #define UI_BIGFONT 0x00000020 // default #define UI_GIANTFONT 0x00000040 #define UI_DROPSHADOW 0x00000800 #define UI_BLINK 0x00001000 #define UI_INVERSE 0x00002000 #define UI_PULSE 0x00004000 #if defined(_DEBUG) && !defined(BSPC) #define HUNK_DEBUG #endif typedef enum { h_high, h_low, h_dontcare } ha_pref; #ifdef HUNK_DEBUG #define Hunk_Alloc( size, preference ) Hunk_AllocDebug(size, preference, #size, __FILE__, __LINE__) void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ); #else void *Hunk_Alloc( int size, ha_pref preference ); #endif #define Com_Memset memset #define Com_Memcpy memcpy #define CIN_system 1 #define CIN_loop 2 #define CIN_hold 4 #define CIN_silent 8 #define CIN_shader 16 /* ============================================================== MATHLIB ============================================================== */ typedef float vec_t; typedef vec_t vec2_t[2]; typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; typedef vec_t vec5_t[5]; typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; #ifndef M_PI #define M_PI 3.14159265358979323846f // matches value in gcc v2 math.h #endif #define NUMVERTEXNORMALS 162 extern vec3_t bytedirs[NUMVERTEXNORMALS]; // all drawing is done to a 640*480 virtual screen size // and will be automatically scaled to the real resolution #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define TINYCHAR_WIDTH (SMALLCHAR_WIDTH) #define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2) #define SMALLCHAR_WIDTH 8 #define SMALLCHAR_HEIGHT 16 #define BIGCHAR_WIDTH 16 #define BIGCHAR_HEIGHT 16 #define GIANTCHAR_WIDTH 32 #define GIANTCHAR_HEIGHT 48 extern vec4_t colorBlack; extern vec4_t colorRed; extern vec4_t colorGreen; extern vec4_t colorBlue; extern vec4_t colorYellow; extern vec4_t colorMagenta; extern vec4_t colorCyan; extern vec4_t colorWhite; extern vec4_t colorLtGrey; extern vec4_t colorMdGrey; extern vec4_t colorDkGrey; #define Q_COLOR_ESCAPE '^' #define Q_IsColorString(p) ((p) && *(p) == Q_COLOR_ESCAPE && *((p)+1) && *((p)+1) >= '0' && *((p)+1) <= '8') // ^[0-8] #define COLOR_BLACK '0' #define COLOR_RED '1' #define COLOR_GREEN '2' #define COLOR_YELLOW '3' #define COLOR_BLUE '4' #define COLOR_CYAN '5' #define COLOR_MAGENTA '6' #define COLOR_WHITE '7' #define COLOR_MENU '8' #define ColorIndex(c) ((c) - '0') #define S_COLOR_BLACK "^0" #define S_COLOR_RED "^1" #define S_COLOR_GREEN "^2" #define S_COLOR_YELLOW "^3" #define S_COLOR_BLUE "^4" #define S_COLOR_CYAN "^5" #define S_COLOR_MAGENTA "^6" #define S_COLOR_WHITE "^7" #define S_COLOR_MENU "^8" extern vec4_t g_color_table[9]; #define MAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b #define MAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a #define DEG2RAD( a ) ( ( (a) * M_PI ) / 180.0F ) #define RAD2DEG( a ) ( ( (a) * 180.0f ) / M_PI ) struct cplane_s; extern vec3_t vec3_origin; extern vec3_t axisDefault[3]; #define nanmask (255<<23) #define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) #if idppc static ID_INLINE float Q_rsqrt( float number ) { float x = 0.5f * number; float y; #ifdef __GNUC__ asm("frsqrte %0,%1" : "=f" (y) : "f" (number)); #else y = __frsqrte( number ); #endif return y * (1.5f - (x * y * y)); } #ifdef __GNUC__ static ID_INLINE float Q_fabs(float x) { float abs_x; asm("fabs %0,%1" : "=f" (abs_x) : "f" (x)); return abs_x; } #else #define Q_fabs __fabsf #endif #else float Q_fabs( float f ); float Q_rsqrt( float f ); // reciprocal square root #endif #define SQRTFAST( x ) ( (x) * Q_rsqrt( x ) ) signed char ClampChar( int i ); signed short ClampShort( int i ); // this isn't a real cheap function to call! int DirToByte( vec3_t dir ); void ByteToDir( int b, vec3_t dir ); #if 1 #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 VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) #define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) #else #define DotProduct(x,y) _DotProduct(x,y) #define VectorSubtract(a,b,c) _VectorSubtract(a,b,c) #define VectorAdd(a,b,c) _VectorAdd(a,b,c) #define VectorCopy(a,b) _VectorCopy(a,b) #define VectorScale(v, s, o) _VectorScale(v,s,o) #define VectorMA(v, s, b, o) _VectorMA(v,s,b,o) #endif #ifdef Q3_VM #ifdef VectorCopy #undef VectorCopy // this is a little hack to get more efficient copies in our interpreter typedef struct { float v[3]; } vec3struct_t; #define VectorCopy(a,b) (*(vec3struct_t *)b=*(vec3struct_t *)a) #endif #endif #define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0) #define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) #define VectorSet(v, x, y, z) ((v)[0]=(x), (v)[1]=(y), (v)[2]=(z)) #define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) #define SnapVector(v) {v[0]=((int)(v[0]));v[1]=((int)(v[1]));v[2]=((int)(v[2]));} // just in case you do't want to use the macros vec_t _DotProduct( const vec3_t v1, const vec3_t v2 ); void _VectorSubtract( const vec3_t veca, const vec3_t vecb, vec3_t out ); void _VectorAdd( const vec3_t veca, const vec3_t vecb, vec3_t out ); void _VectorCopy( const vec3_t in, vec3_t out ); void _VectorScale( const vec3_t in, float scale, vec3_t out ); void _VectorMA( const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc ); unsigned ColorBytes3 (float r, float g, float b); unsigned ColorBytes4 (float r, float g, float b, float a); float NormalizeColor( const vec3_t in, vec3_t out ); float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ); void ClearBounds( vec3_t mins, vec3_t maxs ); void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); #if !defined( Q3_VM ) || ( defined( Q3_VM ) && defined( __Q3_VM_MATH ) ) static ID_INLINE int VectorCompare( const vec3_t v1, const vec3_t v2 ) { if (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]) { return 0; } return 1; } static ID_INLINE vec_t VectorLength( const vec3_t v ) { return (vec_t)sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); } static ID_INLINE vec_t VectorLengthSquared( const vec3_t v ) { return (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); } static ID_INLINE vec_t Distance( const vec3_t p1, const vec3_t p2 ) { vec3_t v; VectorSubtract (p2, p1, v); return VectorLength( v ); } static ID_INLINE vec_t DistanceSquared( const vec3_t p1, const vec3_t p2 ) { vec3_t v; VectorSubtract (p2, p1, v); return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; } // fast vector normalize routine that does not check to make sure // that length != 0, nor does it return length, uses rsqrt approximation static ID_INLINE void VectorNormalizeFast( vec3_t v ) { float ilength; ilength = Q_rsqrt( DotProduct( v, v ) ); v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } static ID_INLINE void VectorInverse( vec3_t v ){ v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } static ID_INLINE void CrossProduct( const vec3_t v1, const 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]; } #else int VectorCompare( const vec3_t v1, const vec3_t v2 ); vec_t VectorLength( const vec3_t v ); vec_t VectorLengthSquared( const vec3_t v ); vec_t Distance( const vec3_t p1, const vec3_t p2 ); vec_t DistanceSquared( const vec3_t p1, const vec3_t p2 ); void VectorNormalizeFast( vec3_t v ); void VectorInverse( vec3_t v ); void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); #endif vec_t VectorNormalize (vec3_t v); // returns vector length vec_t VectorNormalize2( const vec3_t v, vec3_t out ); void Vector4Scale( const vec4_t in, vec_t scale, vec4_t out ); void VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out ); int Q_log2(int val); float Q_acos(float c); int Q_rand( int *seed ); float Q_random( int *seed ); float Q_crandom( int *seed ); #define random() ((rand () & 0x7fff) / ((float)0x7fff)) #define crandom() (2.0 * (random() - 0.5)) void vectoangles( const vec3_t value1, vec3_t angles); void AnglesToAxis( const vec3_t angles, vec3_t axis[3] ); void AxisClear( vec3_t axis[3] ); void AxisCopy( vec3_t in[3], vec3_t out[3] ); void SetPlaneSignbits( struct cplane_s *out ); int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *plane); qboolean BoundsIntersect(const vec3_t mins, const vec3_t maxs, const vec3_t mins2, const vec3_t maxs2); qboolean BoundsIntersectSphere(const vec3_t mins, const vec3_t maxs, const vec3_t origin, vec_t radius); qboolean BoundsIntersectPoint(const vec3_t mins, const vec3_t maxs, const vec3_t origin); float AngleMod(float a); float LerpAngle (float from, float to, float frac); float AngleSubtract( float a1, float a2 ); void AnglesSubtract( vec3_t v1, vec3_t v2, vec3_t v3 ); float AngleNormalize360 ( float angle ); float AngleNormalize180 ( float angle ); float AngleDelta ( float angle1, float angle2 ); qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ); void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); void RotateAroundDirection( vec3_t axis[3], float yaw ); void MakeNormalVectors( const vec3_t forward, vec3_t right, vec3_t up ); // perpendicular vector could be replaced by this //int PlaneTypeForNormal (vec3_t normal); void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]); void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); void PerpendicularVector( vec3_t dst, const vec3_t src ); int Q_isnan( float x ); //============================================= float Com_Clamp( float min, float max, float value ); char *COM_SkipPath( char *pathname ); const char *COM_GetExtension( const char *name ); void COM_StripExtension(const char *in, char *out, int destsize); void COM_DefaultExtension( char *path, int maxSize, const char *extension ); void COM_BeginParseSession( const char *name ); int COM_GetCurrentParseLine( void ); char *COM_Parse( char **data_p ); char *COM_ParseExt( char **data_p, qboolean allowLineBreak ); int COM_Compress( char *data_p ); void COM_ParseError( char *format, ... ) __attribute__ ((format (printf, 1, 2))); void COM_ParseWarning( char *format, ... ) __attribute__ ((format (printf, 1, 2))); //int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ); #define MAX_TOKENLENGTH 1024 #ifndef TT_STRING //token types #define TT_STRING 1 // string #define TT_LITERAL 2 // literal #define TT_NUMBER 3 // number #define TT_NAME 4 // name #define TT_PUNCTUATION 5 // punctuation #endif typedef struct pc_token_s { int type; int subtype; int intvalue; float floatvalue; char string[MAX_TOKENLENGTH]; } pc_token_t; // data is an in/out parm, returns a parsed out token void COM_MatchToken( char**buf_p, char *match ); void SkipBracedSection (char **program); void SkipRestOfLine ( char **data ); void Parse1DMatrix (char **buf_p, int x, float *m); void Parse2DMatrix (char **buf_p, int y, int x, float *m); void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m); void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); char *Com_SkipTokens( char *s, int numTokens, char *sep ); char *Com_SkipCharset( char *s, char *sep ); void Com_RandomBytes( byte *string, int len ); // mode parm for FS_FOpenFile typedef enum { FS_READ, FS_WRITE, FS_APPEND, FS_APPEND_SYNC } fsMode_t; typedef enum { FS_SEEK_CUR, FS_SEEK_END, FS_SEEK_SET } fsOrigin_t; //============================================= int Q_isprint( int c ); int Q_islower( int c ); int Q_isupper( int c ); int Q_isalpha( int c ); // portable case insensitive compare int Q_stricmp (const char *s1, const char *s2); #define Q_strequal(s1,s2) (Q_stricmp(s1,s2)==0) int Q_strncmp (const char *s1, const char *s2, int n); int Q_stricmpn (const char *s1, const char *s2, int n); char *Q_strlwr( char *s1 ); char *Q_strupr( char *s1 ); const char *Q_stristr( const char *s, const char *find); // buffer size safe library replacements /** * Copies a string from one array to another in a safe way * * @param dest (out) pointer to destination array * @param src (in) pointer to source array * @param destsize size of the destination array, at most destsize-1 will be copied */ void Q_strncpyz( char *dest, const char *src, int destsize ); /** * Appends a string to another string. The function protects against overflow. * The size is the max size of the destination AFTER the string has been appended * If the length of dest is larger or equal to destsize then nothing will be appended. * * @param dest (in/out) pointer to the string that will be appended to * @param size size of the destination array * @param src (in) the string to append */ void Q_strcat( char *dest, int size, const char *src ); /** * strlen that counts the length of the string after the color sequences have * been removed. Example: "A^2I" = 2 because it will be printed "AI" * * @param string the string to * @return the length of the string as printed */ int Q_PrintStrlen( const char *string ); /** * Removes all colors from a string. * * @param string (in/out) the string to make color less * @return pointer to the string */ char *Q_CleanStr( char *string ); // Count the number of char tocount encountered in string int Q_CountChar(const char *string, char tocount); //============================================= // 64-bit integers for global rankings interface // implemented as a struct for qvm compatibility typedef struct { byte b0; byte b1; byte b2; byte b3; byte b4; byte b5; byte b6; byte b7; } qint64; //============================================= /* short BigShort(short l); short LittleShort(short l); int BigLong (int l); int LittleLong (int l); qint64 BigLong64 (qint64 l); qint64 LittleLong64 (qint64 l); float BigFloat (const float *l); float LittleFloat (const float *l); void Swap_Init (void); */ char * QDECL va(char *format, ...) __attribute__ ((format (printf, 1, 2))); #define TRUNCATE_LENGTH 64 void Com_TruncateLongString( char *buffer, const char *s ); //============================================= // // key / value info strings // char *Info_ValueForKey( const char *s, const char *key ); void Info_RemoveKey( char *s, const char *key ); void Info_RemoveKey_big( char *s, const char *key ); void Info_SetValueForKey( char *s, const char *key, const char *value ); void Info_SetValueForKey_Big( char *s, const char *key, const char *value ); qboolean Info_Validate( const char *s ); void Info_NextPair( const char **s, char *key, char *value ); // this is only here so the functions in q_shared.c and bg_*.c can link void QDECL Com_Error( int level, const char *error, ... ) __attribute__ ((format (printf, 2, 3))) __attribute__((noreturn)); void QDECL Com_Printf( const char *msg, ... ) __attribute__ ((format (printf, 1, 2))); /* ========================================================== CVARS (console variables) Many variables can be used for cheating purposes, so when cheats is zero, force all unspecified variables to their default values. ========================================================== */ #define CVAR_ARCHIVE 1 // set to cause it to be saved to vars.rc // used for system variables, not for player // specific configurations #define CVAR_USERINFO 2 // sent to server on connect or change #define CVAR_SERVERINFO 4 // sent in response to front end requests #define CVAR_SYSTEMINFO 8 // these cvars will be duplicated on all clients #define CVAR_INIT 16 // don't allow change from console at all, // but can be set from the command line #define CVAR_LATCH 32 // will only change when C code next does // a Cvar_Get(), so it can't be changed // without proper initialization. modified // will be set, even though the value hasn't // changed yet #define CVAR_ROM 64 // display only, cannot be set by user at all #define CVAR_USER_CREATED 128 // created by a set command #define CVAR_TEMP 256 // can be set even when cheats are disabled, but is not archived #define CVAR_CHEAT 512 // can not be changed if cheats are disabled #define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued #define CVAR_SERVER_CREATED 2048 // cvar was created by a server the client connected to. #define CVAR_NONEXISTENT 0xFFFFFFFF // Cvar doesn't exist. // nothing outside the Cvar_*() functions should modify these fields! typedef struct cvar_s { char *name; char *string; char *resetString; // cvar_restart will reset to this value char *latchedString; // for CVAR_LATCH vars int flags; qboolean modified; // set each time the cvar is changed int modificationCount; // incremented each time the cvar is changed float value; // atof( string ) int integer; // atoi( string ) struct cvar_s *next; struct cvar_s *hashNext; } cvar_t; #define MAX_CVAR_VALUE_STRING 256 typedef int cvarHandle_t; // the modules that run in the virtual machine can't access the cvar_t directly, // so they must ask for structured updates typedef struct { cvarHandle_t handle; int modificationCount; float value; int integer; char string[MAX_CVAR_VALUE_STRING]; } vmCvar_t; /* ============================================================== COLLISION DETECTION ============================================================== */ #include "surfaceflags.h" // shared with the q3map utility // plane types are used to speed some tests // 0-2 are axial planes #define PLANE_X 0 #define PLANE_Y 1 #define PLANE_Z 2 #define PLANE_NON_AXIAL 3 /* ================= PlaneTypeForNormal ================= */ #define PlaneTypeForNormal(x) (x[0] == 1.0 ? PLANE_X : (x[1] == 1.0 ? PLANE_Y : (x[2] == 1.0 ? PLANE_Z : PLANE_NON_AXIAL) ) ) // plane_t structure // !!! if this is changed, it must be changed in asm code too !!! typedef struct cplane_s { vec3_t normal; float dist; byte type; // for fast side tests: 0,1,2 = axial, 3 = nonaxial byte signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision byte pad[2]; } cplane_t; // a trace is returned when a box is swept through the world typedef struct { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position cplane_t plane; // surface normal at impact, transformed to world space int surfaceFlags; // surface hit int contents; // contents on other side of surface hit int entityNum; // entity the contacted sirface is a part of } trace_t; // trace->entityNum can also be 0 to (MAX_GENTITIES-1) // or ENTITYNUM_NONE, ENTITYNUM_WORLD // markfragments are returned by CM_MarkFragments() typedef struct { int firstPoint; int numPoints; } markFragment_t; typedef struct { vec3_t origin; vec3_t axis[3]; } orientation_t; //===================================================================== // in order from highest priority to lowest // if none of the catchers are active, bound key strings will be executed #define KEYCATCH_CONSOLE 0x0001 #define KEYCATCH_UI 0x0002 #define KEYCATCH_MESSAGE 0x0004 #define KEYCATCH_CGAME 0x0008 // sound channels // channel 0 never willingly overrides // other channels will allways override a playing sound on that channel typedef enum { CHAN_AUTO, CHAN_LOCAL, // menu sounds, etc CHAN_WEAPON, CHAN_VOICE, CHAN_ITEM, CHAN_BODY, CHAN_LOCAL_SOUND, // chat messages, etc CHAN_ANNOUNCER // announcer voices, etc } soundChannel_t; /* ======================================================================== ELEMENTS COMMUNICATED ACROSS THE NET ======================================================================== */ #define ANGLE2SHORT(x) ((int)((x)*65536/360) & 65535) #define SHORT2ANGLE(x) ((x)*(360.0/65536)) #define SNAPFLAG_RATE_DELAYED 1 #define SNAPFLAG_NOT_ACTIVE 2 // snapshot used during connection and for zombies #define SNAPFLAG_SERVERCOUNT 4 // toggled every map_restart so transitions can be detected // // per-level limits // #define MAX_CLIENTS 64 // absolute limit #define MAX_LOCATIONS 64 #define GENTITYNUM_BITS 10 // don't need to send any more #define MAX_GENTITIES (1<serverTime of last executed command int pm_type; int bobCycle; // for view bobbing and footstep generation int pm_flags; // ducked, jump_held, etc int pm_time; vec3_t origin; vec3_t velocity; int weaponTime; int gravity; int speed; int delta_angles[3]; // add to command angles to get view direction // changed by spawns, rotating objects, and teleporters int groundEntityNum;// ENTITYNUM_NONE = in air int legsTimer; // don't change low priority animations until this runs out int legsAnim; // mask off ANIM_TOGGLEBIT int torsoTimer; // don't change low priority animations until this runs out int torsoAnim; // mask off ANIM_TOGGLEBIT int movementDir; // a number 0 to 7 that represents the reletive angle // of movement to the view angle (axial and diagonals) // when at rest, the value will remain unchanged // used to twist the legs during strafing vec3_t grapplePoint; // location of grapple to pull towards if PMF_GRAPPLE_PULL int eFlags; // copied to entityState_t->eFlags int eventSequence; // pmove generated events int events[MAX_PS_EVENTS]; int eventParms[MAX_PS_EVENTS]; int externalEvent; // events set on player from another source int externalEventParm; int externalEventTime; int clientNum; // ranges from 0 to MAX_CLIENTS-1 int weapon; // copied to entityState_t->weapon int weaponstate; vec3_t viewangles; // for fixed views int viewheight; // damage feedback int damageEvent; // when it changes, latch the other parms int damageYaw; int damagePitch; int damageCount; int stats[MAX_STATS]; int persistant[MAX_PERSISTANT]; // stats that aren't cleared on death int powerups[MAX_POWERUPS]; // level.time that the powerup runs out int ammo[MAX_WEAPONS]; int generic1; int loopSound; int jumppad_ent; // jumppad entity hit this frame // not communicated over the net at all int ping; // server to game info for scoreboard int pmove_framecount; // FIXME: don't transmit over the network int jumppad_frame; int entityEventSequence; } playerState_t; //==================================================================== // // usercmd_t->button bits, many of which are generated by the client system, // so they aren't game/cgame only definitions // #define BUTTON_ATTACK 1 #define BUTTON_TALK 2 // displays talk balloon and disables actions #define BUTTON_USE_HOLDABLE 4 #define BUTTON_GESTURE 8 #define BUTTON_WALKING 16 // walking can't just be infered from MOVE_RUN // because a key pressed late in the frame will // only generate a small move value for that frame // walking will use different animations and // won't generate footsteps #define BUTTON_AFFIRMATIVE 32 #define BUTTON_NEGATIVE 64 #define BUTTON_GETFLAG 128 #define BUTTON_GUARDBASE 256 #define BUTTON_PATROL 512 #define BUTTON_FOLLOWME 1024 #define BUTTON_ANY 2048 // any key whatsoever #define MOVE_RUN 120 // if forwardmove or rightmove are >= MOVE_RUN, // then BUTTON_WALKING should be set // usercmd_t is sent to the server each client frame typedef struct usercmd_s { int serverTime; int angles[3]; int buttons; byte weapon; // weapon signed char forwardmove, rightmove, upmove; } usercmd_t; //=================================================================== // if entityState->solid == SOLID_BMODEL, modelindex is an inline model number #define SOLID_BMODEL 0xffffff typedef enum { TR_STATIONARY, TR_INTERPOLATE, // non-parametric, but interpolate between snapshots TR_LINEAR, TR_LINEAR_STOP, TR_SINE, // value = base + sin( time / duration ) * delta TR_GRAVITY } trType_t; typedef struct { trType_t trType; int trTime; int trDuration; // if non 0, trTime + trDuration = stop time vec3_t trBase; vec3_t trDelta; // velocity, etc } trajectory_t; // entityState_t is the information conveyed from the server // in an update message about entities that the client will // need to render in some way // Different eTypes may use the information in different ways // The messages are delta compressed, so it doesn't really matter if // the structure size is fairly large typedef struct entityState_s { int number; // entity index int eType; // entityType_t int eFlags; trajectory_t pos; // for calculating position trajectory_t apos; // for calculating angles int time; int time2; vec3_t origin; vec3_t origin2; vec3_t angles; vec3_t angles2; int otherEntityNum; // shotgun sources, etc int otherEntityNum2; int groundEntityNum; // -1 = in air int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24) int loopSound; // constantly loop this sound int modelindex; int modelindex2; int clientNum; // 0 to (MAX_CLIENTS - 1), for players and corpses int frame; int solid; // for client side prediction, trap_linkentity sets this properly int event; // impulse events -- muzzle flashes, footsteps, etc int eventParm; // for players int powerups; // bit flags int weapon; // determines weapon and flash model, etc int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT int generic1; } entityState_t; typedef enum { CA_UNINITIALIZED, CA_DISCONNECTED, // not talking to a server CA_AUTHORIZING, // not used any more, was checking cd key CA_CONNECTING, // sending request packets to the server CA_CHALLENGING, // sending challenge packets to the server CA_CONNECTED, // netchan_t established, getting gamestate CA_LOADING, // only during cgame initialization, never during main loop CA_PRIMED, // got gamestate, waiting for first frame CA_ACTIVE, // game views should be displayed CA_CINEMATIC // playing a cinematic or a static pic, not connected to a server } connstate_t; // font support #define GLYPH_START 0 #define GLYPH_END 255 #define GLYPH_CHARSTART 32 #define GLYPH_CHAREND 127 #define GLYPHS_PER_FONT GLYPH_END - GLYPH_START + 1 typedef struct { int height; // number of scan lines int top; // top of glyph in buffer int bottom; // bottom of glyph in buffer int pitch; // width for copying int xSkip; // x adjustment int imageWidth; // width of actual image int imageHeight; // height of actual image float s; // x offset in image where glyph starts float t; // y offset in image where glyph starts float s2; float t2; qhandle_t glyph; // handle to the shader with the glyph char shaderName[32]; } glyphInfo_t; typedef struct { glyphInfo_t glyphs [GLYPHS_PER_FONT]; float glyphScale; char name[MAX_QPATH]; } fontInfo_t; #define Square(x) ((x)*(x)) // real time //============================================= typedef struct qtime_s { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday - [0,6] */ int tm_yday; /* days since January 1 - [0,365] */ int tm_isdst; /* daylight savings time flag */ } qtime_t; // server browser sources // TTimo: AS_MPLAYER is no longer used #define AS_LOCAL 0 #define AS_MPLAYER 1 #define AS_GLOBAL 2 #define AS_FAVORITES 3 // cinematic states typedef enum { FMV_IDLE, FMV_PLAY, // play FMV_EOF, // all other conditions, i.e. stop/EOF/abort FMV_ID_BLT, FMV_ID_IDLE, FMV_LOOPED, FMV_ID_WAIT } e_status; typedef enum _flag_status { FLAG_ATBASE = 0, FLAG_TAKEN, // CTF FLAG_TAKEN_RED, // One Flag CTF FLAG_TAKEN_BLUE, // One Flag CTF FLAG_DROPPED } flagStatus_t; #define MAX_GLOBAL_SERVERS 4096 #define MAX_OTHER_SERVERS 128 #define MAX_PINGREQUESTS 32 #define MAX_SERVERSTATUSREQUESTS 16 #define SAY_ALL 0 #define SAY_TEAM 1 #define SAY_TELL 2 #define CDKEY_LEN 16 #define CDCHKSUM_LEN 2 #endif // __Q_SHARED_H openarena_0.8.8.orig/code/qcommon/q_math.c0000644000175000017500000007226711656310263017243 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // q_math.c -- stateless support routines that are included in each code module // Some of the vector functions are static inline in q_shared.h. q3asm // doesn't understand static functions though, so we only want them in // one file. That's what this is about. #ifdef Q3_VM #define __Q3_VM_MATH #endif #include "q_shared.h" vec3_t vec3_origin = {0,0,0}; vec3_t axisDefault[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; vec4_t colorBlack = {0, 0, 0, 1}; vec4_t colorRed = {1, 0, 0, 1}; vec4_t colorGreen = {0, 1, 0, 1}; vec4_t colorBlue = {0, 0, 1, 1}; vec4_t colorYellow = {1, 1, 0, 1}; vec4_t colorMagenta= {1, 0, 1, 1}; vec4_t colorCyan = {0, 1, 1, 1}; vec4_t colorWhite = {1, 1, 1, 1}; vec4_t colorLtGrey = {0.75, 0.75, 0.75, 1}; vec4_t colorMdGrey = {0.5, 0.5, 0.5, 1}; vec4_t colorDkGrey = {0.25, 0.25, 0.25, 1}; vec4_t g_color_table[9] = { {0.0, 0.0, 0.0, 1.0}, {1.0, 0.0, 0.0, 1.0}, {0.0, 1.0, 0.0, 1.0}, {1.0, 1.0, 0.0, 1.0}, {0.0, 0.0, 1.0, 1.0}, {0.0, 1.0, 1.0, 1.0}, {1.0, 0.0, 1.0, 1.0}, {1.0, 1.0, 1.0, 1.0}, {1.00f, 0.43f, 0.00f, 1.00f}, }; vec3_t bytedirs[NUMVERTEXNORMALS] = { {-0.525731f, 0.000000f, 0.850651f}, {-0.442863f, 0.238856f, 0.864188f}, {-0.295242f, 0.000000f, 0.955423f}, {-0.309017f, 0.500000f, 0.809017f}, {-0.162460f, 0.262866f, 0.951056f}, {0.000000f, 0.000000f, 1.000000f}, {0.000000f, 0.850651f, 0.525731f}, {-0.147621f, 0.716567f, 0.681718f}, {0.147621f, 0.716567f, 0.681718f}, {0.000000f, 0.525731f, 0.850651f}, {0.309017f, 0.500000f, 0.809017f}, {0.525731f, 0.000000f, 0.850651f}, {0.295242f, 0.000000f, 0.955423f}, {0.442863f, 0.238856f, 0.864188f}, {0.162460f, 0.262866f, 0.951056f}, {-0.681718f, 0.147621f, 0.716567f}, {-0.809017f, 0.309017f, 0.500000f},{-0.587785f, 0.425325f, 0.688191f}, {-0.850651f, 0.525731f, 0.000000f},{-0.864188f, 0.442863f, 0.238856f}, {-0.716567f, 0.681718f, 0.147621f},{-0.688191f, 0.587785f, 0.425325f}, {-0.500000f, 0.809017f, 0.309017f}, {-0.238856f, 0.864188f, 0.442863f}, {-0.425325f, 0.688191f, 0.587785f}, {-0.716567f, 0.681718f, -0.147621f}, {-0.500000f, 0.809017f, -0.309017f}, {-0.525731f, 0.850651f, 0.000000f}, {0.000000f, 0.850651f, -0.525731f}, {-0.238856f, 0.864188f, -0.442863f}, {0.000000f, 0.955423f, -0.295242f}, {-0.262866f, 0.951056f, -0.162460f}, {0.000000f, 1.000000f, 0.000000f}, {0.000000f, 0.955423f, 0.295242f}, {-0.262866f, 0.951056f, 0.162460f}, {0.238856f, 0.864188f, 0.442863f}, {0.262866f, 0.951056f, 0.162460f}, {0.500000f, 0.809017f, 0.309017f}, {0.238856f, 0.864188f, -0.442863f},{0.262866f, 0.951056f, -0.162460f}, {0.500000f, 0.809017f, -0.309017f},{0.850651f, 0.525731f, 0.000000f}, {0.716567f, 0.681718f, 0.147621f}, {0.716567f, 0.681718f, -0.147621f}, {0.525731f, 0.850651f, 0.000000f}, {0.425325f, 0.688191f, 0.587785f}, {0.864188f, 0.442863f, 0.238856f}, {0.688191f, 0.587785f, 0.425325f}, {0.809017f, 0.309017f, 0.500000f}, {0.681718f, 0.147621f, 0.716567f}, {0.587785f, 0.425325f, 0.688191f}, {0.955423f, 0.295242f, 0.000000f}, {1.000000f, 0.000000f, 0.000000f}, {0.951056f, 0.162460f, 0.262866f}, {0.850651f, -0.525731f, 0.000000f},{0.955423f, -0.295242f, 0.000000f}, {0.864188f, -0.442863f, 0.238856f}, {0.951056f, -0.162460f, 0.262866f}, {0.809017f, -0.309017f, 0.500000f}, {0.681718f, -0.147621f, 0.716567f}, {0.850651f, 0.000000f, 0.525731f}, {0.864188f, 0.442863f, -0.238856f}, {0.809017f, 0.309017f, -0.500000f}, {0.951056f, 0.162460f, -0.262866f}, {0.525731f, 0.000000f, -0.850651f}, {0.681718f, 0.147621f, -0.716567f}, {0.681718f, -0.147621f, -0.716567f},{0.850651f, 0.000000f, -0.525731f}, {0.809017f, -0.309017f, -0.500000f}, {0.864188f, -0.442863f, -0.238856f}, {0.951056f, -0.162460f, -0.262866f}, {0.147621f, 0.716567f, -0.681718f}, {0.309017f, 0.500000f, -0.809017f}, {0.425325f, 0.688191f, -0.587785f}, {0.442863f, 0.238856f, -0.864188f}, {0.587785f, 0.425325f, -0.688191f}, {0.688191f, 0.587785f, -0.425325f}, {-0.147621f, 0.716567f, -0.681718f}, {-0.309017f, 0.500000f, -0.809017f}, {0.000000f, 0.525731f, -0.850651f}, {-0.525731f, 0.000000f, -0.850651f}, {-0.442863f, 0.238856f, -0.864188f}, {-0.295242f, 0.000000f, -0.955423f}, {-0.162460f, 0.262866f, -0.951056f}, {0.000000f, 0.000000f, -1.000000f}, {0.295242f, 0.000000f, -0.955423f}, {0.162460f, 0.262866f, -0.951056f}, {-0.442863f, -0.238856f, -0.864188f}, {-0.309017f, -0.500000f, -0.809017f}, {-0.162460f, -0.262866f, -0.951056f}, {0.000000f, -0.850651f, -0.525731f}, {-0.147621f, -0.716567f, -0.681718f}, {0.147621f, -0.716567f, -0.681718f}, {0.000000f, -0.525731f, -0.850651f}, {0.309017f, -0.500000f, -0.809017f}, {0.442863f, -0.238856f, -0.864188f}, {0.162460f, -0.262866f, -0.951056f}, {0.238856f, -0.864188f, -0.442863f}, {0.500000f, -0.809017f, -0.309017f}, {0.425325f, -0.688191f, -0.587785f}, {0.716567f, -0.681718f, -0.147621f}, {0.688191f, -0.587785f, -0.425325f}, {0.587785f, -0.425325f, -0.688191f}, {0.000000f, -0.955423f, -0.295242f}, {0.000000f, -1.000000f, 0.000000f}, {0.262866f, -0.951056f, -0.162460f}, {0.000000f, -0.850651f, 0.525731f}, {0.000000f, -0.955423f, 0.295242f}, {0.238856f, -0.864188f, 0.442863f}, {0.262866f, -0.951056f, 0.162460f}, {0.500000f, -0.809017f, 0.309017f}, {0.716567f, -0.681718f, 0.147621f}, {0.525731f, -0.850651f, 0.000000f}, {-0.238856f, -0.864188f, -0.442863f}, {-0.500000f, -0.809017f, -0.309017f}, {-0.262866f, -0.951056f, -0.162460f}, {-0.850651f, -0.525731f, 0.000000f}, {-0.716567f, -0.681718f, -0.147621f}, {-0.716567f, -0.681718f, 0.147621f}, {-0.525731f, -0.850651f, 0.000000f}, {-0.500000f, -0.809017f, 0.309017f}, {-0.238856f, -0.864188f, 0.442863f}, {-0.262866f, -0.951056f, 0.162460f}, {-0.864188f, -0.442863f, 0.238856f}, {-0.809017f, -0.309017f, 0.500000f}, {-0.688191f, -0.587785f, 0.425325f}, {-0.681718f, -0.147621f, 0.716567f}, {-0.442863f, -0.238856f, 0.864188f}, {-0.587785f, -0.425325f, 0.688191f}, {-0.309017f, -0.500000f, 0.809017f}, {-0.147621f, -0.716567f, 0.681718f}, {-0.425325f, -0.688191f, 0.587785f}, {-0.162460f, -0.262866f, 0.951056f}, {0.442863f, -0.238856f, 0.864188f}, {0.162460f, -0.262866f, 0.951056f}, {0.309017f, -0.500000f, 0.809017f}, {0.147621f, -0.716567f, 0.681718f}, {0.000000f, -0.525731f, 0.850651f}, {0.425325f, -0.688191f, 0.587785f}, {0.587785f, -0.425325f, 0.688191f}, {0.688191f, -0.587785f, 0.425325f}, {-0.955423f, 0.295242f, 0.000000f}, {-0.951056f, 0.162460f, 0.262866f}, {-1.000000f, 0.000000f, 0.000000f}, {-0.850651f, 0.000000f, 0.525731f}, {-0.955423f, -0.295242f, 0.000000f}, {-0.951056f, -0.162460f, 0.262866f}, {-0.864188f, 0.442863f, -0.238856f}, {-0.951056f, 0.162460f, -0.262866f}, {-0.809017f, 0.309017f, -0.500000f}, {-0.864188f, -0.442863f, -0.238856f}, {-0.951056f, -0.162460f, -0.262866f}, {-0.809017f, -0.309017f, -0.500000f}, {-0.681718f, 0.147621f, -0.716567f}, {-0.681718f, -0.147621f, -0.716567f}, {-0.850651f, 0.000000f, -0.525731f}, {-0.688191f, 0.587785f, -0.425325f}, {-0.587785f, 0.425325f, -0.688191f}, {-0.425325f, 0.688191f, -0.587785f}, {-0.425325f, -0.688191f, -0.587785f}, {-0.587785f, -0.425325f, -0.688191f}, {-0.688191f, -0.587785f, -0.425325f} }; //============================================================== int Q_rand( int *seed ) { *seed = (69069 * *seed + 1); return *seed; } float Q_random( int *seed ) { return ( Q_rand( seed ) & 0xffff ) / (float)0x10000; } float Q_crandom( int *seed ) { return 2.0 * ( Q_random( seed ) - 0.5 ); } //======================================================= signed char ClampChar( int i ) { if ( i < -128 ) { return -128; } if ( i > 127 ) { return 127; } return i; } signed short ClampShort( int i ) { if ( i < -32768 ) { return -32768; } if ( i > 0x7fff ) { return 0x7fff; } return i; } // this isn't a real cheap function to call! int DirToByte( vec3_t dir ) { int i, best; float d, bestd; if ( !dir ) { return 0; } bestd = 0; best = 0; for (i=0 ; i bestd) { bestd = d; best = i; } } return best; } void ByteToDir( int b, vec3_t dir ) { if ( b < 0 || b >= NUMVERTEXNORMALS ) { VectorCopy( vec3_origin, dir ); return; } VectorCopy (bytedirs[b], dir); } unsigned ColorBytes3 (float r, float g, float b) { unsigned i; ( (byte *)&i )[0] = r * 255; ( (byte *)&i )[1] = g * 255; ( (byte *)&i )[2] = b * 255; return i; } unsigned ColorBytes4 (float r, float g, float b, float a) { unsigned i; ( (byte *)&i )[0] = r * 255; ( (byte *)&i )[1] = g * 255; ( (byte *)&i )[2] = b * 255; ( (byte *)&i )[3] = a * 255; return i; } float NormalizeColor( const vec3_t in, vec3_t out ) { float max; max = in[0]; if ( in[1] > max ) { max = in[1]; } if ( in[2] > max ) { max = in[2]; } if ( !max ) { VectorClear( out ); } else { out[0] = in[0] / max; out[1] = in[1] / max; out[2] = in[2] / max; } return max; } /* ===================== PlaneFromPoints Returns false if the triangle is degenrate. The normal will point out of the clock for clockwise ordered points ===================== */ qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ) { vec3_t d1, d2; VectorSubtract( b, a, d1 ); VectorSubtract( c, a, d2 ); CrossProduct( d2, d1, plane ); if ( VectorNormalize( plane ) == 0 ) { return qfalse; } plane[3] = DotProduct( a, plane ); return qtrue; } /* =============== RotatePointAroundVector This is not implemented very well... =============== */ 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]; int i; vec3_t vr, vup, vf; float rad; 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; rad = DEG2RAD( degrees ); zrot[0][0] = cos( rad ); zrot[0][1] = sin( rad ); zrot[1][0] = -sin( rad ); zrot[1][1] = cos( rad ); MatrixMultiply( m, zrot, tmpmat ); MatrixMultiply( 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]; } } /* =============== RotateAroundDirection =============== */ void RotateAroundDirection( vec3_t axis[3], float yaw ) { // create an arbitrary axis[1] PerpendicularVector( axis[1], axis[0] ); // rotate it around axis[0] by yaw if ( yaw ) { vec3_t temp; VectorCopy( axis[1], temp ); RotatePointAroundVector( axis[1], axis[0], temp, yaw ); } // cross to get axis[2] CrossProduct( axis[0], axis[1], axis[2] ); } void vectoangles( const vec3_t value1, vec3_t angles ) { float forward; float yaw, pitch; if ( value1[1] == 0 && value1[0] == 0 ) { yaw = 0; if ( value1[2] > 0 ) { pitch = 90; } else { pitch = 270; } } else { if ( value1[0] ) { yaw = ( atan2 ( value1[1], value1[0] ) * 180 / M_PI ); } else if ( value1[1] > 0 ) { yaw = 90; } else { yaw = 270; } if ( yaw < 0 ) { yaw += 360; } forward = sqrt ( value1[0]*value1[0] + value1[1]*value1[1] ); pitch = ( atan2(value1[2], forward) * 180 / M_PI ); if ( pitch < 0 ) { pitch += 360; } } angles[PITCH] = -pitch; angles[YAW] = yaw; angles[ROLL] = 0; } /* ================= AnglesToAxis ================= */ void AnglesToAxis( const vec3_t angles, vec3_t axis[3] ) { vec3_t right; // angle vectors returns "right" instead of "y axis" AngleVectors( angles, axis[0], right, axis[2] ); VectorSubtract( vec3_origin, right, axis[1] ); } void AxisClear( vec3_t axis[3] ) { axis[0][0] = 1; axis[0][1] = 0; axis[0][2] = 0; axis[1][0] = 0; axis[1][1] = 1; axis[1][2] = 0; axis[2][0] = 0; axis[2][1] = 0; axis[2][2] = 1; } void AxisCopy( vec3_t in[3], vec3_t out[3] ) { VectorCopy( in[0], out[0] ); VectorCopy( in[1], out[1] ); VectorCopy( in[2], out[2] ); } void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) { float d; vec3_t n; float inv_denom; inv_denom = DotProduct( normal, normal ); #ifndef Q3_VM assert( Q_fabs(inv_denom) != 0.0f ); // zero vectors get here #endif inv_denom = 1.0f / inv_denom; 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]; } /* ================ MakeNormalVectors Given a normalized forward vector, create two other perpendicular vectors ================ */ void MakeNormalVectors( const vec3_t forward, vec3_t right, vec3_t up) { float d; // this rotate and negate guarantees a vector // not colinear with the original right[1] = -forward[0]; right[2] = forward[1]; right[0] = forward[2]; d = DotProduct (right, forward); VectorMA (right, -d, forward, right); VectorNormalize (right); CrossProduct (right, forward, up); } void VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out ) { out[0] = DotProduct( in, matrix[0] ); out[1] = DotProduct( in, matrix[1] ); out[2] = DotProduct( in, matrix[2] ); } //============================================================================ #if !idppc /* ** float q_rsqrt( float number ) */ float Q_rsqrt( float number ) { union { float f; int i; } t; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; t.f = number; t.i = 0x5f3759df - ( t.i >> 1 ); // what the fuck? y = t.f; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } float Q_fabs( float f ) { int tmp = * ( int * ) &f; tmp &= 0x7FFFFFFF; return * ( float * ) &tmp; } #endif //============================================================ /* =============== LerpAngle =============== */ float LerpAngle (float from, float to, float frac) { float a; if ( to - from > 180 ) { to -= 360; } if ( to - from < -180 ) { to += 360; } a = from + frac * (to - from); return a; } /* ================= AngleSubtract Always returns a value from -180 to 180 ================= */ float AngleSubtract( float a1, float a2 ) { float a; a = a1 - a2; while ( a > 180 ) { a -= 360; } while ( a < -180 ) { a += 360; } return a; } void AnglesSubtract( vec3_t v1, vec3_t v2, vec3_t v3 ) { v3[0] = AngleSubtract( v1[0], v2[0] ); v3[1] = AngleSubtract( v1[1], v2[1] ); v3[2] = AngleSubtract( v1[2], v2[2] ); } float AngleMod(float a) { a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); return a; } /* ================= AngleNormalize360 returns angle normalized to the range [0 <= angle < 360] ================= */ float AngleNormalize360 ( float angle ) { return (360.0 / 65536) * ((int)(angle * (65536 / 360.0)) & 65535); } /* ================= AngleNormalize180 returns angle normalized to the range [-180 < angle <= 180] ================= */ float AngleNormalize180 ( float angle ) { angle = AngleNormalize360( angle ); if ( angle > 180.0 ) { angle -= 360.0; } return angle; } /* ================= AngleDelta returns the normalized delta from angle1 to angle2 ================= */ float AngleDelta ( float angle1, float angle2 ) { return AngleNormalize180( angle1 - angle2 ); } //============================================================ /* ================= SetPlaneSignbits ================= */ void SetPlaneSignbits (cplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) { bits |= 1<signbits = bits; } /* ================== BoxOnPlaneSide Returns 1, 2, or 1 + 2 // this is the slow, general version int BoxOnPlaneSide2 (vec3_t emins, vec3_t emaxs, struct cplane_s *p) { int i; float dist1, dist2; int sides; vec3_t corners[2]; for (i=0 ; i<3 ; i++) { if (p->normal[i] < 0) { corners[0][i] = emins[i]; corners[1][i] = emaxs[i]; } else { corners[1][i] = emins[i]; corners[0][i] = emaxs[i]; } } dist1 = DotProduct (p->normal, corners[0]) - p->dist; dist2 = DotProduct (p->normal, corners[1]) - p->dist; sides = 0; if (dist1 >= 0) sides = 1; if (dist2 < 0) sides |= 2; return sides; } ================== */ #if !id386 int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *p) { float dist1, dist2; int sides; // fast axial cases if (p->type < 3) { if (p->dist <= emins[p->type]) return 1; if (p->dist >= emaxs[p->type]) return 2; return 3; } // general case switch (p->signbits) { case 0: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 1: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 2: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 3: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 4: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 5: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 6: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; case 7: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; default: dist1 = dist2 = 0; // shut up compiler break; } sides = 0; if (dist1 >= p->dist) sides = 1; if (dist2 < p->dist) sides |= 2; return sides; } #elif __GNUC__ // use matha.s #else #pragma warning( disable: 4035 ) __declspec( naked ) int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *p) { static int bops_initialized; static int Ljmptab[8]; __asm { push ebx cmp bops_initialized, 1 je initialized mov bops_initialized, 1 mov Ljmptab[0*4], offset Lcase0 mov Ljmptab[1*4], offset Lcase1 mov Ljmptab[2*4], offset Lcase2 mov Ljmptab[3*4], offset Lcase3 mov Ljmptab[4*4], offset Lcase4 mov Ljmptab[5*4], offset Lcase5 mov Ljmptab[6*4], offset Lcase6 mov Ljmptab[7*4], offset Lcase7 initialized: mov edx,dword ptr[4+12+esp] mov ecx,dword ptr[4+4+esp] xor eax,eax mov ebx,dword ptr[4+8+esp] mov al,byte ptr[17+edx] cmp al,8 jge Lerror fld dword ptr[0+edx] fld st(0) jmp dword ptr[Ljmptab+eax*4] Lcase0: fmul dword ptr[ebx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ecx] fxch st(2) fld st(0) fmul dword ptr[4+ebx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ecx] fxch st(2) fld st(0) fmul dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase1: fmul dword ptr[ecx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ebx] fxch st(2) fld st(0) fmul dword ptr[4+ebx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ecx] fxch st(2) fld st(0) fmul dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase2: fmul dword ptr[ebx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ecx] fxch st(2) fld st(0) fmul dword ptr[4+ecx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ebx] fxch st(2) fld st(0) fmul dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase3: fmul dword ptr[ecx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ebx] fxch st(2) fld st(0) fmul dword ptr[4+ecx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ebx] fxch st(2) fld st(0) fmul dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase4: fmul dword ptr[ebx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ecx] fxch st(2) fld st(0) fmul dword ptr[4+ebx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ecx] fxch st(2) fld st(0) fmul dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase5: fmul dword ptr[ecx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ebx] fxch st(2) fld st(0) fmul dword ptr[4+ebx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ecx] fxch st(2) fld st(0) fmul dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase6: fmul dword ptr[ebx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ecx] fxch st(2) fld st(0) fmul dword ptr[4+ecx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ebx] fxch st(2) fld st(0) fmul dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase7: fmul dword ptr[ecx] fld dword ptr[0+4+edx] fxch st(2) fmul dword ptr[ebx] fxch st(2) fld st(0) fmul dword ptr[4+ecx] fld dword ptr[0+8+edx] fxch st(2) fmul dword ptr[4+ebx] fxch st(2) fld st(0) fmul dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) LSetSides: faddp st(2),st(0) fcomp dword ptr[12+edx] xor ecx,ecx fnstsw ax fcomp dword ptr[12+edx] and ah,1 xor ah,1 add cl,ah fnstsw ax and ah,1 add ah,ah add cl,ah pop ebx mov eax,ecx ret Lerror: int 3 } } #pragma warning( default: 4035 ) #endif /* ================= RadiusFromBounds ================= */ float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ) { int i; vec3_t corner; float a, b; for (i=0 ; i<3 ; i++) { a = fabs( mins[i] ); b = fabs( maxs[i] ); corner[i] = a > b ? a : b; } return VectorLength (corner); } void ClearBounds( vec3_t mins, vec3_t maxs ) { mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; } void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ) { if ( v[0] < mins[0] ) { mins[0] = v[0]; } if ( v[0] > maxs[0]) { maxs[0] = v[0]; } if ( v[1] < mins[1] ) { mins[1] = v[1]; } if ( v[1] > maxs[1]) { maxs[1] = v[1]; } if ( v[2] < mins[2] ) { mins[2] = v[2]; } if ( v[2] > maxs[2]) { maxs[2] = v[2]; } } qboolean BoundsIntersect(const vec3_t mins, const vec3_t maxs, const vec3_t mins2, const vec3_t maxs2) { if ( maxs[0] < mins2[0] || maxs[1] < mins2[1] || maxs[2] < mins2[2] || mins[0] > maxs2[0] || mins[1] > maxs2[1] || mins[2] > maxs2[2]) { return qfalse; } return qtrue; } qboolean BoundsIntersectSphere(const vec3_t mins, const vec3_t maxs, const vec3_t origin, vec_t radius) { if ( origin[0] - radius > maxs[0] || origin[0] + radius < mins[0] || origin[1] - radius > maxs[1] || origin[1] + radius < mins[1] || origin[2] - radius > maxs[2] || origin[2] + radius < mins[2]) { return qfalse; } return qtrue; } qboolean BoundsIntersectPoint(const vec3_t mins, const vec3_t maxs, const vec3_t origin) { if ( origin[0] > maxs[0] || origin[0] < mins[0] || origin[1] > maxs[1] || origin[1] < mins[1] || origin[2] > maxs[2] || origin[2] < mins[2]) { return qfalse; } return qtrue; } vec_t VectorNormalize( vec3_t v ) { // NOTE: TTimo - Apple G4 altivec source uses double? float length, 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; } vec_t VectorNormalize2( const vec3_t v, vec3_t out) { float length, ilength; length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; length = sqrt (length); if (length) { ilength = 1/length; out[0] = v[0]*ilength; out[1] = v[1]*ilength; out[2] = v[2]*ilength; } else { VectorClear( out ); } return length; } void _VectorMA( const vec3_t veca, float scale, const 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( const vec3_t v1, const vec3_t v2 ) { return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; } void _VectorSubtract( const vec3_t veca, const 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( const vec3_t veca, const 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( const vec3_t in, vec3_t out ) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } void _VectorScale( const vec3_t in, vec_t scale, vec3_t out ) { out[0] = in[0]*scale; out[1] = in[1]*scale; out[2] = in[2]*scale; } void Vector4Scale( const vec4_t in, vec_t scale, vec4_t out ) { out[0] = in[0]*scale; out[1] = in[1]*scale; out[2] = in[2]*scale; out[3] = in[3]*scale; } int Q_log2( int val ) { int answer; answer = 0; while ( ( val>>=1 ) != 0 ) { answer++; } return answer; } /* ================= PlaneTypeForNormal ================= */ /* int PlaneTypeForNormal (vec3_t normal) { if ( normal[0] == 1.0 ) return PLANE_X; if ( normal[1] == 1.0 ) return PLANE_Y; if ( normal[2] == 1.0 ) return PLANE_Z; return PLANE_NON_AXIAL; } */ /* ================ MatrixMultiply ================ */ void MatrixMultiply(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 AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs 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); if (forward) { forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; } if (right) { right[0] = (-1*sr*sp*cy+-1*cr*-sy); right[1] = (-1*sr*sp*sy+-1*cr*cy); right[2] = -1*sr*cp; } if (up) { up[0] = (cr*sp*cy+-sr*-sy); up[1] = (cr*sp*sy+-sr*cy); up[2] = cr*cp; } } /* ** assumes "src" is normalized */ void PerpendicularVector( vec3_t dst, const vec3_t src ) { int pos; int i; float minelem = 1.0F; vec3_t tempvec; /* ** find the smallest magnitude axially aligned vector */ 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; /* ** project the point onto the plane defined by src */ ProjectPointOnPlane( dst, tempvec, src ); /* ** normalize the result */ VectorNormalize( dst ); } /* ================ Q_isnan Don't pass doubles to this ================ */ int Q_isnan( float x ) { union { float f; unsigned int i; } t; t.f = x; t.i &= 0x7FFFFFFF; t.i = 0x7F800000 - t.i; return (int)( (unsigned int)t.i >> 31 ); } openarena_0.8.8.orig/code/qcommon/qfiles.h0000644000175000017500000003355511656310263017257 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef __QFILES_H__ #define __QFILES_H__ // // qfiles.h: quake file formats // This file must be identical in the quake and utils directories // //Ignore __attribute__ on non-gcc platforms #ifndef __GNUC__ #ifndef __attribute__ #define __attribute__(x) #endif #endif // surface geometry should not exceed these limits #define SHADER_MAX_VERTEXES 1000 #define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) // the maximum size of game relative pathnames #define MAX_QPATH 64 /* ======================================================================== QVM files ======================================================================== */ #define VM_MAGIC 0x12721444 #define VM_MAGIC_VER2 0x12721445 typedef struct { int vmMagic; int instructionCount; int codeOffset; int codeLength; int dataOffset; int dataLength; int litLength; // ( dataLength - litLength ) should be byteswapped on load int bssLength; // zero filled memory appended to datalength //!!! below here is VM_MAGIC_VER2 !!! int jtrgLength; // number of jump table targets } vmHeader_t; /* ======================================================================== .MD3 triangle model file format ======================================================================== */ #define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') #define MD3_VERSION 15 // limits #define MD3_MAX_LODS 3 #define MD3_MAX_TRIANGLES 8192 // per surface #define MD3_MAX_VERTS 4096 // per surface #define MD3_MAX_SHADERS 256 // per surface #define MD3_MAX_FRAMES 1024 // per model #define MD3_MAX_SURFACES 32 // per model #define MD3_MAX_TAGS 16 // per frame // vertex scales #define MD3_XYZ_SCALE (1.0/64) typedef struct md3Frame_s { vec3_t bounds[2]; vec3_t localOrigin; float radius; char name[16]; } md3Frame_t; typedef struct md3Tag_s { char name[MAX_QPATH]; // tag name vec3_t origin; vec3_t axis[3]; } md3Tag_t; /* ** md3Surface_t ** ** CHUNK SIZE ** header sizeof( md3Surface_t ) ** shaders sizeof( md3Shader_t ) * numShaders ** triangles[0] sizeof( md3Triangle_t ) * numTriangles ** st sizeof( md3St_t ) * numVerts ** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames */ typedef struct { int ident; // char name[MAX_QPATH]; // polyset name int flags; int numFrames; // all surfaces in a model should have the same int numShaders; // all surfaces in a model should have the same int numVerts; int numTriangles; int ofsTriangles; int ofsShaders; // offset from start of md3Surface_t int ofsSt; // texture coords are common for all frames int ofsXyzNormals; // numVerts * numFrames int ofsEnd; // next surface follows } md3Surface_t; typedef struct { char name[MAX_QPATH]; int shaderIndex; // for in-game use } md3Shader_t; typedef struct { int indexes[3]; } md3Triangle_t; typedef struct { float st[2]; } md3St_t; typedef struct { short xyz[3]; short normal; } md3XyzNormal_t; typedef struct { int ident; int version; char name[MAX_QPATH]; // model name int flags; int numFrames; int numTags; int numSurfaces; int numSkins; int ofsFrames; // offset for first frame int ofsTags; // numFrames * numTags int ofsSurfaces; // first surface, others follow int ofsEnd; // end of file } md3Header_t; /* ============================================================================== MD4 file format ============================================================================== */ #define MD4_IDENT (('4'<<24)+('P'<<16)+('D'<<8)+'I') #define MD4_VERSION 1 #define MD4_MAX_BONES 128 typedef struct { int boneIndex; // these are indexes into the boneReferences, float boneWeight; // not the global per-frame bone list vec3_t offset; } md4Weight_t; typedef struct { vec3_t normal; vec2_t texCoords; int numWeights; md4Weight_t weights[1]; // variable sized } md4Vertex_t; typedef struct { int indexes[3]; } md4Triangle_t; typedef struct { int ident; char name[MAX_QPATH]; // polyset name char shader[MAX_QPATH]; int shaderIndex; // for in-game use int ofsHeader; // this will be a negative number int numVerts; int ofsVerts; int numTriangles; int ofsTriangles; // Bone references are a set of ints representing all the bones // present in any vertex weights for this surface. This is // needed because a model may have surfaces that need to be // drawn at different sort times, and we don't want to have // to re-interpolate all the bones for each surface. int numBoneReferences; int ofsBoneReferences; int ofsEnd; // next surface follows } md4Surface_t; typedef struct { float matrix[3][4]; } md4Bone_t; typedef struct { vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame vec3_t localOrigin; // midpoint of bounds, used for sphere cull float radius; // dist from localOrigin to corner md4Bone_t bones[1]; // [numBones] } md4Frame_t; typedef struct { int numSurfaces; int ofsSurfaces; // first surface, others follow int ofsEnd; // next lod follows } md4LOD_t; typedef struct { int ident; int version; char name[MAX_QPATH]; // model name // frames and bones are shared by all levels of detail int numFrames; int numBones; int ofsBoneNames; // char name[ MAX_QPATH ] int ofsFrames; // md4Frame_t[numFrames] // each level of detail has completely separate sets of surfaces int numLODs; int ofsLODs; int ofsEnd; // end of file } md4Header_t; /* * Here are the definitions for Ravensoft's model format of md4. Raven stores their * playermodels in .mdr files, in some games, which are pretty much like the md4 * format implemented by ID soft. It seems like ID's original md4 stuff is not used at all. * MDR is being used in EliteForce, JediKnight2 and Soldiers of Fortune2 (I think). * So this comes in handy for anyone who wants to make it possible to load player * models from these games. * This format has bone tags, which is similar to the thing you have in md3 I suppose. * Raven has released their version of md3view under GPL enabling me to add support * to this codebase. Thanks to Steven Howes aka Skinner for helping with example * source code. * * - Thilo Schulz (arny@ats.s.bawue.de) */ // If you want to enable support for Raven's .mdr / md4 format, uncomment the next // line. //#define RAVENMD4 #ifdef RAVENMD4 #define MDR_IDENT (('5'<<24)+('M'<<16)+('D'<<8)+'R') #define MDR_VERSION 2 #define MDR_MAX_BONES 128 typedef struct { int boneIndex; // these are indexes into the boneReferences, float boneWeight; // not the global per-frame bone list vec3_t offset; } mdrWeight_t; typedef struct { vec3_t normal; vec2_t texCoords; int numWeights; mdrWeight_t weights[1]; // variable sized } mdrVertex_t; typedef struct { int indexes[3]; } mdrTriangle_t; typedef struct { int ident; char name[MAX_QPATH]; // polyset name char shader[MAX_QPATH]; int shaderIndex; // for in-game use int ofsHeader; // this will be a negative number int numVerts; int ofsVerts; int numTriangles; int ofsTriangles; // Bone references are a set of ints representing all the bones // present in any vertex weights for this surface. This is // needed because a model may have surfaces that need to be // drawn at different sort times, and we don't want to have // to re-interpolate all the bones for each surface. int numBoneReferences; int ofsBoneReferences; int ofsEnd; // next surface follows } mdrSurface_t; typedef struct { float matrix[3][4]; } mdrBone_t; typedef struct { vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame vec3_t localOrigin; // midpoint of bounds, used for sphere cull float radius; // dist from localOrigin to corner char name[16]; mdrBone_t bones[1]; // [numBones] } mdrFrame_t; typedef struct { unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple } mdrCompBone_t; typedef struct { vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame vec3_t localOrigin; // midpoint of bounds, used for sphere cull float radius; // dist from localOrigin to corner mdrCompBone_t bones[1]; // [numBones] } mdrCompFrame_t; typedef struct { int numSurfaces; int ofsSurfaces; // first surface, others follow int ofsEnd; // next lod follows } mdrLOD_t; typedef struct { int boneIndex; char name[32]; } mdrTag_t; typedef struct { int ident; int version; char name[MAX_QPATH]; // model name // frames and bones are shared by all levels of detail int numFrames; int numBones; int ofsFrames; // mdrFrame_t[numFrames] // each level of detail has completely separate sets of surfaces int numLODs; int ofsLODs; int numTags; int ofsTags; int ofsEnd; // end of file } mdrHeader_t; #endif /* ============================================================================== .BSP file format ============================================================================== */ #define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') // little-endian "IBSP" #define BSP_VERSION 46 // there shouldn't be any problem with increasing these values at the // expense of more memory allocation in the utilities #define MAX_MAP_MODELS 0x400 #define MAX_MAP_BRUSHES 0x8000 #define MAX_MAP_ENTITIES 0x800 #define MAX_MAP_ENTSTRING 0x40000 #define MAX_MAP_SHADERS 0x400 #define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! #define MAX_MAP_FOGS 0x100 #define MAX_MAP_PLANES 0x20000 #define MAX_MAP_NODES 0x20000 #define MAX_MAP_BRUSHSIDES 0x20000 #define MAX_MAP_LEAFS 0x20000 #define MAX_MAP_LEAFFACES 0x20000 #define MAX_MAP_LEAFBRUSHES 0x40000 #define MAX_MAP_PORTALS 0x20000 #define MAX_MAP_LIGHTING 0x800000 #define MAX_MAP_LIGHTGRID 0x800000 #define MAX_MAP_VISIBILITY 0x200000 #define MAX_MAP_DRAW_SURFS 0x20000 #define MAX_MAP_DRAW_VERTS 0x80000 #define MAX_MAP_DRAW_INDEXES 0x80000 // key / value pair sizes in the entities lump #define MAX_KEY 32 #define MAX_VALUE 1024 // the editor uses these predefined yaw angles to orient entities up or down #define ANGLE_UP -1 #define ANGLE_DOWN -2 #define LIGHTMAP_WIDTH 128 #define LIGHTMAP_HEIGHT 128 #define MAX_WORLD_COORD ( 128*1024 ) #define MIN_WORLD_COORD ( -128*1024 ) #define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD ) //============================================================================= typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_SHADERS 1 #define LUMP_PLANES 2 #define LUMP_NODES 3 #define LUMP_LEAFS 4 #define LUMP_LEAFSURFACES 5 #define LUMP_LEAFBRUSHES 6 #define LUMP_MODELS 7 #define LUMP_BRUSHES 8 #define LUMP_BRUSHSIDES 9 #define LUMP_DRAWVERTS 10 #define LUMP_DRAWINDEXES 11 #define LUMP_FOGS 12 #define LUMP_SURFACES 13 #define LUMP_LIGHTMAPS 14 #define LUMP_LIGHTGRID 15 #define LUMP_VISIBILITY 16 #define HEADER_LUMPS 17 typedef struct { int ident; int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { float mins[3], maxs[3]; int firstSurface, numSurfaces; int firstBrush, numBrushes; } dmodel_t; typedef struct { char shader[MAX_QPATH]; int surfaceFlags; int contentFlags; } dshader_t; // planes x^1 is allways the opposite of plane x typedef struct { float normal[3]; float dist; } dplane_t; typedef struct { int planeNum; int children[2]; // negative numbers are -(leafs+1), not nodes int mins[3]; // for frustom culling int maxs[3]; } dnode_t; typedef struct { int cluster; // -1 = opaque cluster (do I still store these?) int area; int mins[3]; // for frustum culling int maxs[3]; int firstLeafSurface; int numLeafSurfaces; int firstLeafBrush; int numLeafBrushes; } dleaf_t; typedef struct { int planeNum; // positive plane side faces out of the leaf int shaderNum; } dbrushside_t; typedef struct { int firstSide; int numSides; int shaderNum; // the shader that determines the contents flags } dbrush_t; typedef struct { char shader[MAX_QPATH]; int brushNum; int visibleSide; // the brush side that ray tests need to clip against (-1 == none) } dfog_t; typedef struct { vec3_t xyz; float st[2]; float lightmap[2]; vec3_t normal; byte color[4]; } drawVert_t; #define drawVert_t_cleared(x) drawVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0, 0}} typedef enum { MST_BAD, MST_PLANAR, MST_PATCH, MST_TRIANGLE_SOUP, MST_FLARE } mapSurfaceType_t; typedef struct { int shaderNum; int fogNum; int surfaceType; int firstVert; int numVerts; int firstIndex; int numIndexes; int lightmapNum; int lightmapX, lightmapY; int lightmapWidth, lightmapHeight; vec3_t lightmapOrigin; vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds int patchWidth; int patchHeight; } dsurface_t; #endif openarena_0.8.8.orig/code/qcommon/q_platform.h0000644000175000017500000001662111656310263020133 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef __Q_PLATFORM_H #define __Q_PLATFORM_H // this is for determining if we have an asm version of a C function #ifdef Q3_VM #define id386 0 #define idppc 0 #define idppc_altivec 0 #else #if (defined _M_IX86 || defined __i386__) && !defined(C_ONLY) #define id386 1 #else #define id386 0 #endif #if (defined(powerc) || defined(powerpc) || defined(ppc) || \ defined(__ppc) || defined(__ppc__)) && !defined(C_ONLY) #define idppc 1 #if defined(__VEC__) #define idppc_altivec 1 #ifdef MACOS_X // Apple's GCC does this differently than the FSF. #define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ (vector unsigned char) (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) #else #define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ (vector unsigned char) {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p} #endif #else #define idppc_altivec 0 #endif #else #define idppc 0 #define idppc_altivec 0 #endif #endif #ifndef __ASM_I386__ // don't include the C bits if included from qasm.h // for windows fastcall option #define QDECL //================================================================= WIN32 === #ifdef __MINGW32__ #undef QDECL #define QDECL __cdecl #if defined( _MSC_VER ) #define OS_STRING "win_msvc" #elif defined __MINGW32__ #define OS_STRING "win_mingw" #endif #define ID_INLINE __inline #define PATH_SEP '\\' #if defined( _M_IX86 ) || defined( __i386__ ) #define ARCH_STRING "x86" #elif defined _M_ALPHA #define ARCH_STRING "AXP" #endif #define Q3_LITTLE_ENDIAN #define DLL_EXT ".dll" #endif //KK-OAX, This is a little hack to be able to build dll's for debugging under Visual Studio //Simple copy/paste/rename of the #ifdef __MINGW32__ #ifdef VSTUDIO #undef QDECL #define QDECL __cdecl #if defined( _MSC_VER ) #define OS_STRING "win_msvc" #elif defined __MINGW32__ #define OS_STRING "win_mingw" #endif #define ID_INLINE __inline #define PATH_SEP '\\' #if defined( _M_IX86 ) || defined( __i386__ ) #define ARCH_STRING "x86" #elif defined _M_ALPHA #define ARCH_STRING "AXP" #endif #define Q3_LITTLE_ENDIAN #define DLL_EXT ".dll" #endif //============================================================== MAC OS X === #if defined(MACOS_X) || defined(__APPLE_CC__) // make sure this is defined, just for sanity's sake... #ifndef MACOS_X #define MACOS_X #endif #define OS_STRING "macosx" #define ID_INLINE inline #define PATH_SEP '/' #ifdef __ppc__ #define ARCH_STRING "ppc" #define Q3_BIG_ENDIAN #elif defined __i386__ #define ARCH_STRING "i386" #define Q3_LITTLE_ENDIAN #endif #define DLL_EXT ".dylib" #endif //================================================================= LINUX === #ifdef __linux__ #include #define OS_STRING "linux" #define ID_INLINE inline #define PATH_SEP '/' #if defined __i386__ #define ARCH_STRING "i386" #elif defined __x86_64__ #define ARCH_STRING "x86_64" #elif defined __powerpc64__ #define ARCH_STRING "ppc64" #elif defined __powerpc__ #define ARCH_STRING "ppc" #elif defined __s390__ #define ARCH_STRING "s390" #elif defined __s390x__ #define ARCH_STRING "s390x" #elif defined __ia64__ #define ARCH_STRING "ia64" #elif defined __alpha__ #define ARCH_STRING "alpha" #elif defined __sparc__ #define ARCH_STRING "sparc" #elif defined __arm__ #define ARCH_STRING "arm" #elif defined __cris__ #define ARCH_STRING "cris" #elif defined __hppa__ #define ARCH_STRING "hppa" #elif defined __mips__ #define ARCH_STRING "mips" #elif defined __sh__ #define ARCH_STRING "sh" #endif #if __FLOAT_WORD_ORDER == __BIG_ENDIAN #define Q3_BIG_ENDIAN #else #define Q3_LITTLE_ENDIAN #endif #define DLL_EXT ".so" #endif //=================================================================== BSD === #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) #include #include #ifndef __BSD__ #define __BSD__ #endif #if defined(__FreeBSD__) #define OS_STRING "freebsd" #elif defined(__OpenBSD__) #define OS_STRING "openbsd" #elif defined(__NetBSD__) #define OS_STRING "netbsd" #endif #define ID_INLINE inline #define PATH_SEP '/' #ifdef __i386__ #define ARCH_STRING "i386" #elif defined __axp__ #define ARCH_STRING "alpha" #endif #if BYTE_ORDER == BIG_ENDIAN #define Q3_BIG_ENDIAN #else #define Q3_LITTLE_ENDIAN #endif #define DLL_EXT ".so" #endif //================================================================= SUNOS === #ifdef __sun #include #include #define OS_STRING "solaris" #define ID_INLINE inline #define PATH_SEP '/' #ifdef __i386__ #define ARCH_STRING "i386" #elif defined __sparc #define ARCH_STRING "sparc" #endif #if defined( _BIG_ENDIAN ) #define Q3_BIG_ENDIAN #elif defined( _LITTLE_ENDIAN ) #define Q3_LITTLE_ENDIAN #endif #define DLL_EXT ".so" #endif //================================================================== IRIX === #ifdef __sgi #define OS_STRING "irix" #define ID_INLINE __inline #define PATH_SEP '/' #define ARCH_STRING "mips" #define Q3_BIG_ENDIAN // SGI's MIPS are always big endian #define DLL_EXT ".so" #endif //================================================================== Q3VM === #ifdef Q3_VM #define OS_STRING "q3vm" #define ID_INLINE #define PATH_SEP '/' #define ARCH_STRING "bytecode" #define DLL_EXT ".qvm" #endif //=========================================================================== //catch missing defines in above blocks #if !defined( OS_STRING ) #error "Operating system not supported" #endif #if !defined( ARCH_STRING ) #error "Architecture not supported" #endif #ifndef ID_INLINE #error "ID_INLINE not defined" #endif #ifndef PATH_SEP #error "PATH_SEP not defined" #endif #ifndef DLL_EXT #error "DLL_EXT not defined" #endif //endianness short ShortSwap (short l); int LongSwap (int l); float FloatSwap (const float *f); #if defined( Q3_BIG_ENDIAN ) && defined( Q3_LITTLE_ENDIAN ) #error "Endianness defined as both big and little" #elif defined( Q3_BIG_ENDIAN ) #define LittleShort(x) ShortSwap(x) #define LittleLong(x) LongSwap(x) #define LittleFloat(x) FloatSwap(&x) #define BigShort #define BigLong #define BigFloat #elif defined( Q3_LITTLE_ENDIAN ) #define LittleShort #define LittleLong #define LittleFloat #define BigShort(x) ShortSwap(x) #define BigLong(x) LongSwap(x) #define BigFloat(x) FloatSwap(&x) #elif defined( Q3_VM ) #define LittleShort #define LittleLong #define LittleFloat #define BigShort #define BigLong #define BigFloat #else #error "Endianness not defined" #endif //platform string #ifdef NDEBUG #define PLATFORM_STRING OS_STRING "-" ARCH_STRING #else #define PLATFORM_STRING OS_STRING "-" ARCH_STRING "-debug" #endif #endif #endif openarena_0.8.8.orig/code/qcommon/qcommon.h0000644000175000017500000010375311656310263017443 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // qcommon.h -- definitions common between client and server, but not game.or ref modules #ifndef _QCOMMON_H_ #define _QCOMMON_H_ #include "../qcommon/cm_public.h" //Ignore __attribute__ on non-gcc platforms #ifndef __GNUC__ #ifndef __attribute__ #define __attribute__(x) #endif #endif //#define PRE_RELEASE_DEMO //============================================================================ // // msg.c // typedef struct { qboolean allowoverflow; // if false, do a Com_Error qboolean overflowed; // set to true if the buffer size failed (with allowoverflow set) qboolean oob; // set to true if the buffer size failed (with allowoverflow set) byte *data; int maxsize; int cursize; int readcount; int bit; // for bitwise reads and writes } msg_t; void MSG_Init (msg_t *buf, byte *data, int length); void MSG_InitOOB( msg_t *buf, byte *data, int length ); void MSG_Clear (msg_t *buf); void MSG_WriteData (msg_t *buf, const void *data, int length); void MSG_Bitstream( msg_t *buf ); // TTimo // copy a msg_t in case we need to store it as is for a bit // (as I needed this to keep an msg_t from a static var for later use) // sets data buffer as MSG_Init does prior to do the copy void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src); struct usercmd_s; struct entityState_s; struct playerState_s; void MSG_WriteBits( msg_t *msg, int value, int bits ); void MSG_WriteChar (msg_t *sb, int c); void MSG_WriteByte (msg_t *sb, int c); void MSG_WriteShort (msg_t *sb, int c); void MSG_WriteLong (msg_t *sb, int c); void MSG_WriteFloat (msg_t *sb, float f); void MSG_WriteString (msg_t *sb, const char *s); void MSG_WriteBigString (msg_t *sb, const char *s); void MSG_WriteAngle16 (msg_t *sb, float f); void MSG_BeginReading (msg_t *sb); void MSG_BeginReadingOOB(msg_t *sb); int MSG_ReadBits( msg_t *msg, int bits ); int MSG_ReadChar (msg_t *sb); int MSG_ReadByte (msg_t *sb); int MSG_ReadShort (msg_t *sb); int MSG_ReadLong (msg_t *sb); float MSG_ReadFloat (msg_t *sb); char *MSG_ReadString (msg_t *sb); char *MSG_ReadBigString (msg_t *sb); char *MSG_ReadStringLine (msg_t *sb); float MSG_ReadAngle16 (msg_t *sb); void MSG_ReadData (msg_t *sb, void *buffer, int size); int MSG_LookaheadByte (msg_t *msg); void MSG_WriteDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); void MSG_ReadDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to , qboolean force ); void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, int number ); void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); void MSG_ReadDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); void MSG_ReportChangeVectors_f( void ); //============================================================================ /* ============================================================== NET ============================================================== */ #define PACKET_BACKUP 32 // number of old messages that must be kept on client and // server for delta comrpession and ping estimation #define PACKET_MASK (PACKET_BACKUP-1) #define MAX_PACKET_USERCMDS 32 // max number of usercmd_t in a packet #define PORT_ANY -1 #define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit typedef enum { NA_BOT, NA_BAD, // an address lookup failed NA_LOOPBACK, NA_BROADCAST, NA_IP, NA_IP6, NA_MULTICAST6, NA_UNSPEC } netadrtype_t; typedef enum { NS_CLIENT, NS_SERVER } netsrc_t; #define NET_ADDRSTRMAXLEN 48 // maximum length of an IPv6 address string including trailing '\0' typedef struct { netadrtype_t type; byte ip[4]; byte ip6[16]; unsigned short port; } netadr_t; void NET_Init( void ); void NET_Shutdown( void ); void NET_Restart( void ); void NET_Config( qboolean enableNetworking ); void NET_FlushPacketQueue(void); void NET_SendPacket (netsrc_t sock, int length, const void *data, netadr_t to); void QDECL NET_OutOfBandPrint( netsrc_t net_socket, netadr_t adr, const char *format, ...) __attribute__ ((format (printf, 3, 4))); void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ); qboolean NET_CompareAdr (netadr_t a, netadr_t b); qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); qboolean NET_IsLocalAddress (netadr_t adr); const char *NET_AdrToString (netadr_t a); const char *NET_AdrToStringwPort (netadr_t a); int NET_StringToAdr ( const char *s, netadr_t *a, netadrtype_t family); qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); void NET_JoinMulticast6(void); void NET_LeaveMulticast6(void); void NET_Sleep(int msec); #define MAX_MSGLEN 16384 // max length of a message, which may // be fragmented into multiple packets #define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames #define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks /* Netchan handles packet fragmentation and out of order / duplicate suppression */ typedef struct { netsrc_t sock; int dropped; // between last packet and previous netadr_t remoteAddress; int qport; // qport value to write when transmitting // sequencing variables int incomingSequence; int outgoingSequence; // incoming fragment assembly buffer int fragmentSequence; int fragmentLength; byte fragmentBuffer[MAX_MSGLEN]; // outgoing fragment buffer // we need to space out the sending of large fragmented messages qboolean unsentFragments; int unsentFragmentStart; int unsentLength; byte unsentBuffer[MAX_MSGLEN]; } netchan_t; void Netchan_Init( int qport ); void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ); void Netchan_Transmit( netchan_t *chan, int length, const byte *data ); void Netchan_TransmitNextFragment( netchan_t *chan ); qboolean Netchan_Process( netchan_t *chan, msg_t *msg ); /* ============================================================== PROTOCOL ============================================================== */ //Open Arena up to 0.7.6 used 68 //Open Arena 0.7.7 uses 69 //Open Arena 0.8.0 used protocol 70 //Open Arena 0.8.1+ uses protocol 71 #define PROTOCOL_VERSION 71 // 1.31 - 67 // maintain a list of compatible protocols for demo playing // NOTE: that stuff only works with two digits protocols extern int demo_protocols[]; #define UPDATE_SERVER_NAME "" // override on command line, config files etc. #ifndef MASTER_SERVER_NAME #define MASTER_SERVER_NAME "dpmaster.deathmask.net" #endif #ifndef STANDALONE #ifndef AUTHORIZE_SERVER_NAME #define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" #endif #ifndef PORT_AUTHORIZE #define PORT_AUTHORIZE 27952 #endif #endif #define PORT_MASTER 27950 #define PORT_UPDATE 27951 #define PORT_SERVER 27960 #define NUM_SERVER_PORTS 4 // broadcast scan this many ports after // PORT_SERVER so a single machine can // run multiple servers // the svc_strings[] array in cl_parse.c should mirror this // // server to client // enum svc_ops_e { svc_bad, svc_nop, svc_gamestate, svc_configstring, // [short] [string] only in gamestate messages svc_baseline, // only in gamestate messages svc_serverCommand, // [string] to be executed by client game module svc_download, // [short] size [size bytes] svc_snapshot, svc_EOF, // svc_extension follows a svc_EOF, followed by another svc_* ... // this keeps legacy clients compatible. svc_extension, svc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; // // client to server // enum clc_ops_e { clc_bad, clc_nop, clc_move, // [[usercmd_t] clc_moveNoDelta, // [[usercmd_t] clc_clientCommand, // [string] message clc_EOF, // clc_extension follows a clc_EOF, followed by another clc_* ... // this keeps legacy servers compatible. clc_extension, clc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; /* ============================================================== VIRTUAL MACHINE ============================================================== */ typedef struct vm_s vm_t; typedef enum { VMI_NATIVE, VMI_BYTECODE, VMI_COMPILED } vmInterpret_t; typedef enum { TRAP_MEMSET = 100, TRAP_MEMCPY, TRAP_STRNCPY, TRAP_SIN, TRAP_COS, TRAP_ATAN2, TRAP_SQRT, TRAP_MATRIXMULTIPLY, TRAP_ANGLEVECTORS, TRAP_PERPENDICULARVECTOR, TRAP_FLOOR, TRAP_CEIL, TRAP_TESTPRINTINT, TRAP_TESTPRINTFLOAT } sharedTraps_t; void VM_Init( void ); vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ); // module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" void VM_Free( vm_t *vm ); void VM_Clear(void); void VM_Forced_Unload_Start(void); void VM_Forced_Unload_Done(void); vm_t *VM_Restart( vm_t *vm ); intptr_t QDECL VM_Call( vm_t *vm, int callNum, ... ); void VM_Debug( int level ); void *VM_ArgPtr( intptr_t intValue ); void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ); #define VMA(x) VM_ArgPtr(args[x]) static ID_INLINE float _vmf(intptr_t x) { floatint_t fi; fi.i = (int) x; return fi.f; } #define VMF(x) _vmf(args[x]) /* ============================================================== CMD Command text buffering and command execution ============================================================== */ /* Any number of commands can be added in a frame, from several different sources. Most commands come from either keybindings or console line input, but entire text files can be execed. */ void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed void Cbuf_AddText( const char *text ); // Adds command text at the end of the buffer, does NOT add a final \n void Cbuf_ExecuteText( int exec_when, const char *text ); // this can be used in place of either Cbuf_AddText or Cbuf_InsertText void Cbuf_Execute (void); // Pulls off \n terminated lines of text from the command buffer and sends // them through Cmd_ExecuteString. Stops when the buffer is empty. // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function, or current args will be destroyed. //=========================================================================== /* Command execution takes a null terminated string, breaks it into tokens, then searches for a command or variable that matches the first token. */ typedef void (*xcommand_t) (void); void Cmd_Init (void); void Cmd_AddCommand( const char *cmd_name, xcommand_t function ); // called by the init functions of other parts of the program to // register commands and functions to call for them. // The cmd_name is referenced later, so it should not be in temp memory // if function is NULL, the command will be forwarded to the server // as a clc_clientCommand instead of executed locally void Cmd_RemoveCommand( const char *cmd_name ); void Cmd_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string int Cmd_Argc (void); char *Cmd_Argv (int arg); void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ); char *Cmd_Args (void); char *Cmd_ArgsFrom( int arg ); void Cmd_ArgsBuffer( char *buffer, int bufferLength ); char *Cmd_Cmd (void); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are allways safe. void Cmd_TokenizeString( const char *text ); void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString( const char *text ); // Parses a single line of text into arguments and tries to execute it // as if it was typed at the console /* ============================================================== CVAR ============================================================== */ /* cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly in C code. The user can access cvars from the console in three ways: r_draworder prints the current value r_draworder 0 sets the current value to 0 set r_draworder 0 as above, but creates the cvar if not present Cvars are restricted from having the same names as commands to keep this interface from being ambiguous. The are also occasionally used to communicated information between different modules of the program. */ cvar_t *Cvar_Get( const char *var_name, const char *value, int flags ); // creates the variable if it doesn't exist, or returns the existing one // if it exists, the value will not be changed, but flags will be ORed in // that allows variables to be unarchived without needing bitflags // if value is "", the value will not override a previously set value. void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); // basically a slightly modified Cvar_Get for the interpreted modules void Cvar_Update( vmCvar_t *vmCvar ); // updates an interpreted modules' version of a cvar void Cvar_Set( const char *var_name, const char *value ); // will create the variable with no flags if it doesn't exist void Cvar_SetLatched( const char *var_name, const char *value); // don't set the cvar immediately void Cvar_SetValue( const char *var_name, float value ); // expands value to a string and calls Cvar_Set float Cvar_VariableValue( const char *var_name ); int Cvar_VariableIntegerValue( const char *var_name ); // returns 0 if not defined or non numeric char *Cvar_VariableString( const char *var_name ); void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); // returns an empty string if not defined int Cvar_Flags(const char *var_name); // returns CVAR_NONEXISTENT if cvar doesn't exist or the flags of that particular CVAR. void Cvar_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string void Cvar_Reset( const char *var_name ); void Cvar_ForceReset(const char *var_name); void Cvar_SetCheatState( void ); // reset all testing vars to a safe value qboolean Cvar_Command( void ); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) void Cvar_WriteVariables( fileHandle_t f ); // writes lines containing "set variable value" for all variables // with the archive flag set to true. void Cvar_Init( void ); char *Cvar_InfoString( int bit ); char *Cvar_InfoString_Big( int bit ); // returns an info string containing all the cvars that have the given bit set // in their flags ( CVAR_USERINFO, CVAR_SERVERINFO, CVAR_SYSTEMINFO, etc ) void Cvar_InfoStringBuffer( int bit, char *buff, int buffsize ); void Cvar_Restart_f( void ); extern int cvar_modifiedFlags; // whenever a cvar is modifed, its flags will be OR'd into this, so // a single check can determine if any CVAR_USERINFO, CVAR_SERVERINFO, // etc, variables have been modified since the last check. The bit // can then be cleared to allow another change detection. /* ============================================================== FILESYSTEM No stdio calls should be used by any part of the game, because we need to deal with all sorts of directory and seperator char issues. ============================================================== */ // referenced flags // these are in loop specific order so don't change the order #define FS_GENERAL_REF 0x01 #define FS_UI_REF 0x02 #define FS_CGAME_REF 0x04 #define FS_QAGAME_REF 0x08 // number of id paks that will never be autodownloaded from baseq3 #define NUM_ID_PAKS 9 #define MAX_FILE_HANDLES 64 #ifdef DEDICATED # define Q3CONFIG_CFG "q3config_server.cfg" #else # define Q3CONFIG_CFG "q3config.cfg" #endif qboolean FS_Initialized( void ); void FS_InitFilesystem ( void ); void FS_Shutdown( qboolean closemfp ); qboolean FS_ConditionalRestart( int checksumFeed ); void FS_Restart( int checksumFeed ); // shutdown and restart the filesystem so changes to fs_gamedir can take effect void FS_AddGameDirectory( const char *path, const char *dir ); char **FS_ListFiles( const char *directory, const char *extension, int *numfiles ); // directory should not have either a leading or trailing / // if extension is "/", only subdirectories will be returned // the returned files will not include any directories or / void FS_FreeFileList( char **list ); qboolean FS_FileExists( const char *file ); char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); int FS_LoadStack( void ); int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); int FS_GetModList( char *listbuf, int bufsize ); fileHandle_t FS_FOpenFileWrite( const char *qpath ); fileHandle_t FS_FOpenFileAppend( const char *filename ); // will properly create any needed paths and deal with seperater character issues int FS_filelength( fileHandle_t f ); fileHandle_t FS_SV_FOpenFileWrite( const char *filename ); int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ); void FS_SV_Rename( const char *from, const char *to ); int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE ); // if uniqueFILE is true, then a new FILE will be fopened even if the file // is found in an already open pak file. If uniqueFILE is false, you must call // FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed // It is generally safe to always set uniqueFILE to true, because the majority of // file IO goes through FS_ReadFile, which Does The Right Thing already. int FS_FileIsInPAK(const char *filename, int *pChecksum ); // returns 1 if a file is in the PAK file, otherwise -1 int FS_Write( const void *buffer, int len, fileHandle_t f ); int FS_Read2( void *buffer, int len, fileHandle_t f ); int FS_Read( void *buffer, int len, fileHandle_t f ); // properly handles partial reads and reads from other dlls void FS_FCloseFile( fileHandle_t f ); // note: you can't just fclose from another DLL, due to MS libc issues int FS_ReadFile( const char *qpath, void **buffer ); // returns the length of the file // a null buffer will just return the file length without loading // as a quick check for existance. -1 length == not present // A 0 byte will always be appended at the end, so string ops are safe. // the buffer should be considered read-only, because it may be cached // for other uses. void FS_ForceFlush( fileHandle_t f ); // forces flush on files we're writing to. void FS_FreeFile( void *buffer ); // frees the memory returned by FS_ReadFile void FS_WriteFile( const char *qpath, const void *buffer, int size ); // writes a complete file, creating any subdirectories needed int FS_filelength( fileHandle_t f ); // doesn't work for files that are opened from a pack file int FS_FTell( fileHandle_t f ); // where are we? void FS_Flush( fileHandle_t f ); void QDECL FS_Printf( fileHandle_t f, const char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); // like fprintf int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ); // opens a file for reading, writing, or appending depending on the value of mode int FS_Seek( fileHandle_t f, long offset, int origin ); // seek on a file (doesn't work for zip files!!!!!!!!) qboolean FS_FilenameCompare( const char *s1, const char *s2 ); const char *FS_GamePureChecksum( void ); // Returns the checksum of the pk3 from which the server loaded the qagame.qvm const char *FS_LoadedPakNames( void ); const char *FS_LoadedPakChecksums( void ); const char *FS_LoadedPakPureChecksums( void ); // Returns a space separated string containing the checksums of all loaded pk3 files. // Servers with sv_pure set will get this string and pass it to clients. const char *FS_ReferencedPakNames( void ); const char *FS_ReferencedPakChecksums( void ); const char *FS_ReferencedPakPureChecksums( void ); // Returns a space separated string containing the checksums of all loaded // AND referenced pk3 files. Servers with sv_pure set will get this string // back from clients for pure validation void FS_ClearPakReferences( int flags ); // clears referenced booleans on loaded pk3s void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ); void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ); // If the string is empty, all data sources will be allowed. // If not empty, only pk3 files that match one of the space // separated checksums will be checked for files, with the // sole exception of .cfg files. qboolean FS_CheckDirTraversal(const char *checkdir); qboolean FS_idPak( char *pak, char *base ); qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); void FS_Rename( const char *from, const char *to ); void FS_Remove( const char *osPath ); void FS_HomeRemove( const char *homePath ); void FS_FilenameCompletion( const char *dir, const char *ext, qboolean stripExt, void(*callback)(const char *s) ); /* ============================================================== Edit fields and command line history/completion ============================================================== */ #define MAX_EDIT_LINE 256 typedef struct { int cursor; int scroll; int widthInChars; char buffer[MAX_EDIT_LINE]; } field_t; void Field_Clear( field_t *edit ); void Field_AutoComplete( field_t *edit ); /* ============================================================== MISC ============================================================== */ // centralizing the declarations for cl_cdkey // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 extern char cl_cdkey[34]; // returned by Sys_GetProcessorFeatures typedef enum { CF_RDTSC = 1 << 0, CF_MMX = 1 << 1, CF_MMX_EXT = 1 << 2, CF_3DNOW = 1 << 3, CF_3DNOW_EXT = 1 << 4, CF_SSE = 1 << 5, CF_SSE2 = 1 << 6, CF_ALTIVEC = 1 << 7 } cpuFeatures_t; // centralized and cleaned, that's the max string you can send to a Com_Printf / Com_DPrintf (above gets truncated) #define MAXPRINTMSG 4096 typedef enum { // SE_NONE must be zero SE_NONE = 0, // evTime is still valid SE_KEY, // evValue is a key code, evValue2 is the down flag SE_CHAR, // evValue is an ascii char SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127) SE_CONSOLE, // evPtr is a char* SE_PACKET // evPtr is a netadr_t followed by data bytes to evPtrLength } sysEventType_t; typedef struct { int evTime; sysEventType_t evType; int evValue, evValue2; int evPtrLength; // bytes of data pointed to by evPtr, for journaling void *evPtr; // this must be manually freed if not NULL } sysEvent_t; void Com_QueueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); int Com_EventLoop( void ); sysEvent_t Com_GetSystemEvent( void ); char *CopyString( const char *in ); void Info_Print( const char *s ); void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)(char *)); void Com_EndRedirect( void ); void QDECL Com_Printf( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2))); void QDECL Com_DPrintf( const char *fmt, ... ) __attribute__ ((format (printf, 1, 2))); void QDECL Com_Error( int code, const char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); void Com_Quit_f( void ); int Com_Milliseconds( void ); // will be journaled properly unsigned Com_BlockChecksum( const void *buffer, int length ); char *Com_MD5File(const char *filename, int length, const char *prefix, int prefix_len); int Com_HashKey(char *string, int maxlen); int Com_Filter(char *filter, char *name, int casesensitive); int Com_FilterPath(char *filter, char *name, int casesensitive); int Com_RealTime(qtime_t *qtime); qboolean Com_SafeMode( void ); void Com_StartupVariable( const char *match ); // checks for and removes command line "+set var arg" constructs // if match is NULL, all set commands will be executed, otherwise // only a set with the exact name. Only used during startup. extern cvar_t *com_developer; extern cvar_t *com_dedicated; extern cvar_t *com_speeds; extern cvar_t *com_timescale; extern cvar_t *com_sv_running; extern cvar_t *com_cl_running; extern cvar_t *com_version; extern cvar_t *com_blood; extern cvar_t *com_buildScript; // for building release pak files extern cvar_t *com_journal; extern cvar_t *com_cameraMode; extern cvar_t *com_ansiColor; extern cvar_t *com_unfocused; extern cvar_t *com_minimized; extern cvar_t *com_altivec; // both client and server must agree to pause extern cvar_t *cl_paused; extern cvar_t *sv_paused; extern cvar_t *cl_packetdelay; extern cvar_t *sv_packetdelay; // com_speeds times extern int time_game; extern int time_frontend; extern int time_backend; // renderer backend time extern int com_frameTime; extern int com_frameMsec; extern qboolean com_errorEntered; extern fileHandle_t com_journalFile; extern fileHandle_t com_journalDataFile; typedef enum { TAG_FREE, TAG_GENERAL, TAG_BOTLIB, TAG_RENDERER, TAG_SMALL, TAG_STATIC } memtag_t; /* --- low memory ---- server vm server clipmap ---mark--- renderer initialization (shaders, etc) UI vm cgame vm renderer map renderer models ---free--- temp file loading --- high memory --- */ #if defined(_DEBUG) && !defined(BSPC) #define ZONE_DEBUG #endif #ifdef ZONE_DEBUG #define Z_TagMalloc(size, tag) Z_TagMallocDebug(size, tag, #size, __FILE__, __LINE__) #define Z_Malloc(size) Z_MallocDebug(size, #size, __FILE__, __LINE__) #define S_Malloc(size) S_MallocDebug(size, #size, __FILE__, __LINE__) void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ); // NOT 0 filled memory void *Z_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory void *S_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory #else void *Z_TagMalloc( int size, int tag ); // NOT 0 filled memory void *Z_Malloc( int size ); // returns 0 filled memory void *S_Malloc( int size ); // NOT 0 filled memory only for small allocations #endif void Z_Free( void *ptr ); void Z_FreeTags( int tag ); int Z_AvailableMemory( void ); void Z_LogHeap( void ); void Hunk_Clear( void ); void Hunk_ClearToMark( void ); void Hunk_SetMark( void ); qboolean Hunk_CheckMark( void ); void Hunk_ClearTempMemory( void ); void *Hunk_AllocateTempMemory( int size ); void Hunk_FreeTempMemory( void *buf ); int Hunk_MemoryRemaining( void ); void Hunk_Log( void); void Hunk_Trash( void ); void Com_TouchMemory( void ); // commandLine should not include the executable name (argv[0]) void Com_Init( char *commandLine ); void Com_Frame( void ); void Com_Shutdown( void ); /* ============================================================== CLIENT / SERVER SYSTEMS ============================================================== */ // // client interface // void CL_InitKeyCommands( void ); // the keyboard binding interface must be setup before execing // config files, but the rest of client startup will happen later void CL_Init( void ); void CL_Disconnect( qboolean showMainMenu ); void CL_Shutdown( void ); void CL_Frame( int msec ); qboolean CL_GameCommand( void ); void CL_KeyEvent (int key, qboolean down, unsigned time); void CL_CharEvent( int key ); // char events are for field typing, not game control void CL_MouseEvent( int dx, int dy, int time ); void CL_JoystickEvent( int axis, int value, int time ); void CL_PacketEvent( netadr_t from, msg_t *msg ); void CL_ConsolePrint( char *text ); void CL_MapLoading( void ); // do a screen update before starting to load a map // when the server is going to load a new map, the entire hunk // will be cleared, so the client must shutdown cgame, ui, and // the renderer void CL_ForwardCommandToServer( const char *string ); // adds the current command line as a clc_clientCommand to the client message. // things like godmode, noclip, etc, are commands directed to the server, // so when they are typed in at the console, they will need to be forwarded. void CL_CDDialog( void ); // bring up the "need a cd to play" dialog void CL_ShutdownAll( void ); // shutdown all the client stuff void CL_FlushMemory( void ); // dump all memory on an error void CL_StartHunkUsers( qboolean rendererOnly ); // start all the client stuff using the hunk void Key_KeynameCompletion( void(*callback)(const char *s) ); // for keyname autocompletion void Key_WriteBindings( fileHandle_t f ); // for writing the config files void S_ClearSoundBuffer( void ); // call before filesystem access void SCR_DebugGraph (float value, int color); // FIXME: move logging to common? // // server interface // void SV_Init( void ); void SV_Shutdown( char *finalmsg ); void SV_Frame( int msec ); void SV_PacketEvent( netadr_t from, msg_t *msg ); qboolean SV_GameCommand( void ); // // UI interface // qboolean UI_GameCommand( void ); qboolean UI_usesUniqueCDKey(void); /* ============================================================== NON-PORTABLE SYSTEM SERVICES ============================================================== */ typedef enum { AXIS_SIDE, AXIS_FORWARD, AXIS_UP, AXIS_ROLL, AXIS_YAW, AXIS_PITCH, MAX_JOYSTICK_AXIS } joystickAxis_t; void Sys_Init (void); // general development dll loading for virtual machine testing void * QDECL Sys_LoadDll( const char *name, char *fqpath , intptr_t (QDECL **entryPoint)(int, ...), intptr_t (QDECL *systemcalls)(intptr_t, ...) ); void Sys_UnloadDll( void *dllHandle ); void Sys_UnloadGame( void ); void *Sys_GetGameAPI( void *parms ); void Sys_UnloadCGame( void ); void *Sys_GetCGameAPI( void ); void Sys_UnloadUI( void ); void *Sys_GetUIAPI( void ); //bot libraries void Sys_UnloadBotLib( void ); void *Sys_GetBotLibAPI( void *parms ); char *Sys_GetCurrentUser( void ); void QDECL Sys_Error( const char *error, ...) __attribute__ ((format (printf, 1, 2))); void Sys_Quit (void); char *Sys_GetClipboardData( void ); // note that this isn't journaled... void Sys_Print( const char *msg ); // Sys_Milliseconds should only be used for profiling purposes, // any game related timing information should come from event timestamps int Sys_Milliseconds (void); void Sys_SnapVector( float *v ); qboolean Sys_RandomBytes( byte *string, int len ); // the system console is shown when a dedicated server is running void Sys_DisplaySystemConsole( qboolean show ); cpuFeatures_t Sys_GetProcessorFeatures( void ); void Sys_SetErrorText( const char *text ); void Sys_SendPacket( int length, const void *data, netadr_t to ); qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ); qboolean Sys_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ); //Does NOT parse port numbers, only base addresses. qboolean Sys_IsLANAddress (netadr_t adr); void Sys_ShowIP(void); void Sys_Mkdir( const char *path ); char *Sys_Cwd( void ); void Sys_SetDefaultInstallPath(const char *path); char *Sys_DefaultInstallPath(void); #ifdef MACOS_X char *Sys_DefaultAppPath(void); #endif void Sys_SetDefaultHomePath(const char *path); char *Sys_DefaultHomePath(void); const char *Sys_Dirname( char *path ); const char *Sys_Basename( char *path ); char *Sys_ConsoleInput(void); char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ); void Sys_FreeFileList( char **list ); void Sys_Sleep(int msec); qboolean Sys_LowPhysicalMemory( void ); /* This is based on the Adaptive Huffman algorithm described in Sayood's Data * Compression book. The ranks are not actually stored, but implicitly defined * by the location of a node within a doubly-linked list */ #define NYT HMAX /* NYT = Not Yet Transmitted */ #define INTERNAL_NODE (HMAX+1) typedef struct nodetype { struct nodetype *left, *right, *parent; /* tree structure */ struct nodetype *next, *prev; /* doubly-linked list */ struct nodetype **head; /* highest ranked node in block */ int weight; int symbol; } node_t; #define HMAX 256 /* Maximum symbol */ typedef struct { int blocNode; int blocPtrs; node_t* tree; node_t* lhead; node_t* ltail; node_t* loc[HMAX+1]; node_t** freelist; node_t nodeList[768]; node_t* nodePtrs[768]; } huff_t; typedef struct { huff_t compressor; huff_t decompressor; } huffman_t; void Huff_Compress(msg_t *buf, int offset); void Huff_Decompress(msg_t *buf, int offset); void Huff_Init(huffman_t *huff); void Huff_addRef(huff_t* huff, byte ch); int Huff_Receive (node_t *node, int *ch, byte *fin); void Huff_transmit (huff_t *huff, int ch, byte *fout); void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset); void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset); void Huff_putBit( int bit, byte *fout, int *offset); int Huff_getBit( byte *fout, int *offset); // don't use if you don't know what you're doing. int Huff_getBloc(void); void Huff_setBloc(int _bloc); extern huffman_t clientHuffTables; #define SV_ENCODE_START 4 #define SV_DECODE_START 12 #define CL_ENCODE_START 12 #define CL_DECODE_START 4 // flags for sv_allowDownload and cl_allowDownload #define DLF_ENABLE 1 #define DLF_NO_REDIRECT 2 #define DLF_NO_UDP 4 #define DLF_NO_DISCONNECT 8 #endif // _QCOMMON_H_ openarena_0.8.8.orig/code/game/0000755000175000017500000000000011720470203015041 5ustar smcvsmcvopenarena_0.8.8.orig/code/game/ai_chat.c0000644000175000017500000010373111656310264016612 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_chat.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_chat.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #ifdef MISSIONPACK // bk001205 #include "../../ui/menudef.h" #endif #define TIME_BETWEENCHATTING 25 /* ================== BotNumActivePlayers ================== */ int BotNumActivePlayers(void) { int i, num; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // num++; } return num; } /* ================== BotIsFirstInRankings ================== */ int BotIsFirstInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); score = bs->cur_ps.persistant[PERS_SCORE]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (score < ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } /* ================== BotIsLastInRankings ================== */ int BotIsLastInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); score = bs->cur_ps.persistant[PERS_SCORE]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (score > ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } /* ================== BotFirstClientInRankings ================== */ char *BotFirstClientInRankings(void) { int i, bestscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); bestscore = -999999; bestclient = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (ps.persistant[PERS_SCORE] > bestscore) { bestscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName(bestclient, name, 32); return name; } /* ================== BotLastClientInRankings ================== */ char *BotLastClientInRankings(void) { int i, worstscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); worstscore = 999999; bestclient = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (ps.persistant[PERS_SCORE] < worstscore) { worstscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName(bestclient, name, 32); return name; } /* ================== BotRandomOpponentName ================== */ char *BotRandomOpponentName(bot_state_t *bs) { int i, count; char buf[MAX_INFO_STRING]; int opponents[MAX_CLIENTS], numopponents; static int maxclients; static char name[32]; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numopponents = 0; opponents[0] = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; //skip team mates if (BotSameTeam(bs, i)) continue; // opponents[numopponents] = i; numopponents++; } count = random() * numopponents; for (i = 0; i < numopponents; i++) { count--; if (count <= 0) { EasyClientName(opponents[i], name, sizeof(name)); return name; } } EasyClientName(opponents[0], name, sizeof(name)); return name; } /* ================== BotMapTitle ================== */ char *BotMapTitle(void) { char info[1024]; static char mapname[128]; trap_GetServerinfo(info, sizeof(info)); strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; return mapname; } /* ================== BotWeaponNameForMeansOfDeath ================== */ char *BotWeaponNameForMeansOfDeath(int mod) { switch(mod) { case MOD_SHOTGUN: return "Shotgun"; case MOD_GAUNTLET: return "Gauntlet"; case MOD_MACHINEGUN: return "Machinegun"; case MOD_GRENADE: case MOD_GRENADE_SPLASH: return "Grenade Launcher"; case MOD_ROCKET: case MOD_ROCKET_SPLASH: return "Rocket Launcher"; case MOD_PLASMA: case MOD_PLASMA_SPLASH: return "Plasmagun"; case MOD_RAILGUN: return "Railgun"; case MOD_LIGHTNING: return "Lightning Gun"; case MOD_BFG: case MOD_BFG_SPLASH: return "BFG10K"; case MOD_NAIL: return "Nailgun"; case MOD_CHAINGUN: return "Chaingun"; case MOD_PROXIMITY_MINE: return "Proximity Launcher"; case MOD_KAMIKAZE: return "Kamikaze"; case MOD_JUICED: return "Prox mine"; case MOD_GRAPPLE: return "Grapple"; default: return "[unknown weapon]"; } } /* ================== BotRandomWeaponName ================== */ char *BotRandomWeaponName(void) { int rnd; rnd = random() * 11.9; switch(rnd) { case 0: return "Gauntlet"; case 1: return "Shotgun"; case 2: return "Machinegun"; case 3: return "Grenade Launcher"; case 4: return "Rocket Launcher"; case 5: return "Plasmagun"; case 6: return "Railgun"; case 7: return "Lightning Gun"; case 8: return "Nailgun"; case 9: return "Chaingun"; case 10: return "Proximity Launcher"; default: return "BFG10K"; } } /* ================== BotVisibleEnemies ================== */ int BotVisibleEnemies(bot_state_t *bs) { float vis; int i; aas_entityinfo_t entinfo; for (i = 0; i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; //if the enemy is invisible and not shooting if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } //if on the same team if (BotSameTeam(bs, i)) continue; //check if the enemy is visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis > 0) return qtrue; } return qfalse; } /* ================== BotValidChatPosition ================== */ int BotValidChatPosition(bot_state_t *bs) { vec3_t point, start, end, mins, maxs; bsp_trace_t trace; //if the bot is dead all positions are valid if (BotIsDead(bs)) return qtrue; if (BotIsObserver(bs)) return qtrue; //never start chatting with a powerup if (bs->inventory[INVENTORY_QUAD] || bs->inventory[INVENTORY_ENVIRONMENTSUIT] || bs->inventory[INVENTORY_HASTE] || bs->inventory[INVENTORY_INVISIBILITY] || bs->inventory[INVENTORY_REGEN] || bs->inventory[INVENTORY_FLIGHT]) return qfalse; //must be on the ground //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse; //do not chat if in lava or slime VectorCopy(bs->origin, point); point[2] -= 24; if (trap_PointContents(point,bs->entitynum) & (CONTENTS_LAVA|CONTENTS_SLIME)) return qfalse; //do not chat if under water VectorCopy(bs->origin, point); point[2] += 32; if (trap_PointContents(point,bs->entitynum) & MASK_WATER) return qfalse; //must be standing on the world entity VectorCopy(bs->origin, start); VectorCopy(bs->origin, end); start[2] += 1; end[2] -= 10; trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); BotAI_Trace(&trace, start, mins, maxs, end, bs->client, MASK_SOLID); if (trace.ent != ENTITYNUM_WORLD) return qfalse; //the bot is in a position where it can chat return qtrue; } /* ================== BotChat_EnterGame ================== */ int BotChat_EnterGame(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; if (!BotValidChatPosition(bs)) return qfalse; BotAI_BotInitialChat(bs, "game_enter", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_ExitGame ================== */ int BotChat_ExitGame(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // BotAI_BotInitialChat(bs, "game_exit", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_StartLevel ================== */ int BotChat_StartLevel(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; } // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; BotAI_BotInitialChat(bs, "level_start", EasyClientName(bs->client, name, 32), // 0 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_EndLevel ================== */ int BotChat_EndLevel(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; // teamplay if (TeamPlayIsOn()) { if (BotIsFirstInRankings(bs)) { trap_EA_Command(bs->client, "vtaunt"); } return qtrue; } // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (BotIsFirstInRankings(bs)) { BotAI_BotInitialChat(bs, "level_end_victory", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); } else if (BotIsLastInRankings(bs)) { BotAI_BotInitialChat(bs, "level_end_lose", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); } else { BotAI_BotInitialChat(bs, "level_end", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); } bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_Death ================== */ int BotChat_Death(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chatting is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS) EasyClientName(bs->lastkilledby, name, 32); else strcpy(name, "[world]"); // if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledby)) { if (bs->lastkilledby == bs->client) return qfalse; BotAI_BotInitialChat(bs, "death_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qtrue; } // if (bs->botdeathtype == MOD_WATER) BotAI_BotInitialChat(bs, "death_drown", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_SLIME) BotAI_BotInitialChat(bs, "death_slime", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_LAVA) BotAI_BotInitialChat(bs, "death_lava", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_FALLING) BotAI_BotInitialChat(bs, "death_cratered", BotRandomOpponentName(bs), NULL); else if (bs->botsuicide || //all other suicides by own weapon bs->botdeathtype == MOD_CRUSH || bs->botdeathtype == MOD_SUICIDE || bs->botdeathtype == MOD_TARGET_LASER || bs->botdeathtype == MOD_TRIGGER_HURT || bs->botdeathtype == MOD_UNKNOWN) BotAI_BotInitialChat(bs, "death_suicide", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_TELEFRAG) BotAI_BotInitialChat(bs, "death_telefrag", name, NULL); else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "death_kamikaze")) BotAI_BotInitialChat(bs, "death_kamikaze", name, NULL); else { if ((bs->botdeathtype == MOD_GAUNTLET || bs->botdeathtype == MOD_RAILGUN || bs->botdeathtype == MOD_BFG || bs->botdeathtype == MOD_BFG_SPLASH) && random() < 0.5) { if (bs->botdeathtype == MOD_GAUNTLET) BotAI_BotInitialChat(bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else if (bs->botdeathtype == MOD_RAILGUN) BotAI_BotInitialChat(bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else BotAI_BotInitialChat(bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } else { BotAI_BotInitialChat(bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } } bs->chatto = CHAT_ALL; } bs->lastchat_time = FloatTime(); return qtrue; } /* ================== BotChat_Kill ================== */ int BotChat_Kill(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (bs->lastkilledplayer == bs->client) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // EasyClientName(bs->lastkilledplayer, name, 32); // bs->chatto = CHAT_ALL; if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledplayer)) { BotAI_BotInitialChat(bs, "kill_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //don't chat in teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; // don't wait } // if (bs->enemydeathtype == MOD_GAUNTLET) { BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL); } else if (bs->enemydeathtype == MOD_RAILGUN) { BotAI_BotInitialChat(bs, "kill_rail", name, NULL); } else if (bs->enemydeathtype == MOD_TELEFRAG) { BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL); } else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "kill_kamikaze")) BotAI_BotInitialChat(bs, "kill_kamikaze", name, NULL); //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "kill_insult", name, NULL); } else { BotAI_BotInitialChat(bs, "kill_praise", name, NULL); } } bs->lastchat_time = FloatTime(); return qtrue; } /* ================== BotChat_EnemySuicide ================== */ int BotChat_EnemySuicide(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENEMYSUICIDE, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // if (bs->enemy >= 0) EasyClientName(bs->enemy, name, 32); else strcpy(name, ""); BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitTalking ================== */ int BotChat_HitTalking(bot_state_t *bs) { char name[32], *weap; int lasthurt_client; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; lasthurt_client = g_entities[bs->client].client->lasthurt_client; if (!lasthurt_client) return qfalse; if (lasthurt_client == bs->client) return qfalse; // if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITTALKING, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_mod); // BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitNoDeath ================== */ int BotChat_HitNoDeath(bot_state_t *bs) { char name[32], *weap; float rnd; int lasthurt_client; aas_entityinfo_t entinfo; lasthurt_client = g_entities[bs->client].client->lasthurt_client; if (!lasthurt_client) return qfalse; if (lasthurt_client == bs->client) return qfalse; // if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse; // if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNODEATH, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsShooting(&entinfo)) return qfalse; // ClientName(lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_mod); // BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitNoKill ================== */ int BotChat_HitNoKill(bot_state_t *bs) { char name[32], *weap; float rnd; aas_entityinfo_t entinfo; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNOKILL, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsShooting(&entinfo)) return qfalse; // ClientName(bs->enemy, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->enemy].client->lasthurt_mod); // BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_Random ================== */ int BotChat_Random(bot_state_t *bs) { float rnd; char name[32]; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //don't chat when doing something important :) if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_RUSHBASE) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_RANDOM, 0, 1); if (random() > bs->thinktime * 0.1) return qfalse; if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; if (random() > 0.25) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // if (bs->lastkilledplayer == bs->client) { Q_strncpyz(name, BotRandomOpponentName(bs),sizeof(name)); } else { EasyClientName(bs->lastkilledplayer, name, sizeof(name)); } if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; // don't wait } // if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_MISC, 0, 1)) { BotAI_BotInitialChat(bs, "random_misc", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); } else { BotAI_BotInitialChat(bs, "random_insult", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); } bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChatTime ================== */ float BotChatTime(bot_state_t *bs) { int cpm; cpm = trap_Characteristic_BInteger(bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000); return 2.0; //(float) trap_BotChatLength(bs->cs) * 30 / cpm; } /* ================== BotChatTest ================== */ void BotChatTest(bot_state_t *bs) { char name[32]; char *weap; int num, i; if (bot_nochat.integer) return; num = trap_BotNumInitialChats(bs->cs, "game_enter"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "game_enter", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "game_exit"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "game_exit", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_start"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_start", EasyClientName(bs->client, name, 32), // 0 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end_victory"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end_victory", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end_lose"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end_lose", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } EasyClientName(bs->lastkilledby, name, sizeof(name)); num = trap_BotNumInitialChats(bs->cs, "death_drown"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "death_drown", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_slime"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_slime", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_lava"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_lava", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_cratered"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_cratered", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_suicide"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_suicide", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_telefrag"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_telefrag", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_gauntlet"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_rail"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_bfg"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_praise"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } // EasyClientName(bs->lastkilledplayer, name, 32); // num = trap_BotNumInitialChats(bs->cs, "kill_gauntlet"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_rail"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_rail", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_telefrag"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_insult", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_praise"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_praise", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "enemy_suicide"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client); num = trap_BotNumInitialChats(bs->cs, "hit_talking"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "hit_nodeath"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "hit_nokill"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } // if (bs->lastkilledplayer == bs->client) { Q_strncpyz(name, BotRandomOpponentName(bs), sizeof(name)); } else { EasyClientName(bs->lastkilledplayer, name, sizeof(name)); } // num = trap_BotNumInitialChats(bs->cs, "random_misc"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "random_misc", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "random_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "random_insult", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } } openarena_0.8.8.orig/code/game/g_rankings.c0000644000175000017500000006450411656310264017350 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // g_rankings.c -- reports for global rankings system #include "g_local.h" #include "g_rankings.h" /* ================ G_RankRunFrame ================ */ void G_RankRunFrame() { gentity_t* ent; gentity_t* ent2; grank_status_t old_status; grank_status_t status; int time; int i; int j; if( !trap_RankCheckInit() ) { trap_RankBegin( GR_GAMEKEY ); } trap_RankPoll(); if( trap_RankActive() ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if ( !ent->inuse ) continue; if ( ent->client == NULL ) continue; if ( ent->r.svFlags & SVF_BOT) { // no bots in ranked games trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n", ent->client->pers.netname) ); continue; } old_status = ent->client->client_status; status = trap_RankUserStatus( i ); if( ent->client->client_status != status ) { // inform client of current status // not needed for client side log in trap_SendServerCommand( i, va("rank_status %i\n",status) ); if ( i == 0 ) { int j = 0; } ent->client->client_status = status; } switch( status ) { case QGR_STATUS_NEW: case QGR_STATUS_SPECTATOR: if( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->sess.spectatorState = SPECTATOR_FREE; ClientSpawn( ent ); // make sure by now CS_GRAND rankingsGameID is ready trap_SendServerCommand( i, va("rank_status %i\n",status) ); trap_SendServerCommand( i, "rank_menu\n" ); } break; case QGR_STATUS_NO_USER: case QGR_STATUS_BAD_PASSWORD: case QGR_STATUS_TIMEOUT: case QGR_STATUS_NO_MEMBERSHIP: case QGR_STATUS_INVALIDUSER: case QGR_STATUS_ERROR: if( (ent->r.svFlags & SVF_BOT) == 0 ) { trap_RankUserReset( ent->s.clientNum ); } break; case QGR_STATUS_ACTIVE: if( (ent->client->sess.sessionTeam == TEAM_SPECTATOR || (client->isEliminated)) && (g_gametype.integer < GT_TEAM) ) { SetTeam( ent, "free" ); } if( old_status != QGR_STATUS_ACTIVE ) { // player has just become active for( j = 0; j < level.maxclients; j++ ) { ent2 = &(g_entities[j]); if ( !ent2->inuse ) continue; if ( ent2->client == NULL ) continue; if ( ent2->r.svFlags & SVF_BOT) continue; if( (i != j) && (trap_RankUserStatus( j ) == QGR_STATUS_ACTIVE) ) { trap_RankReportInt( i, j, QGR_KEY_PLAYED_WITH, 1, 0 ); } // send current scores so the player's rank will show // up under the crosshair immediately DeathmatchScoreboardMessage( ent2 ); } } break; default: break; } } // don't let ranked games last forever if( ((g_fraglimit.integer == 0) || (g_fraglimit.integer > 100)) && ((g_timelimit.integer == 0) || (g_timelimit.integer > 1000)) ) { trap_Cvar_Set( "timelimit", "1000" ); } } // tell time to clients so they can show current match rating if( level.intermissiontime == 0 ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if( ent->client == NULL ) { continue; } time = (level.time - ent->client->pers.enterTime) / 1000; ent->client->ps.persistant[PERS_MATCH_TIME] = time; } } } /* ================ G_RankFireWeapon ================ */ void G_RankFireWeapon( int self, int weapon ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } if( weapon == WP_GAUNTLET ) { // the gauntlet only "fires" when it actually hits something return; } trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED, 1, 1 ); switch( weapon ) { case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_MACHINEGUN, 1, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_SHOTGUN, 1, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRENADE, 1, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_ROCKET, 1, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_LIGHTNING, 1, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_RAILGUN, 1, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_PLASMA, 1, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_BFG, 1, 1 ); break; case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRAPPLE, 1, 1 ); break; default: break; } } /* ================ G_RankDamage ================ */ void G_RankDamage( int self, int attacker, int damage, int means_of_death ) { // state information to avoid counting each shotgun pellet as a hit static int last_framenum = -1; static int last_self = -1; static int last_attacker = -1; static int last_means_of_death = MOD_UNKNOWN; qboolean new_hit; int splash; int key_hit; int key_damage; int key_splash; if( level.warmupTime != 0 ) { // no reports during warmup period return; } new_hit = (level.framenum != last_framenum) || (self != last_self) || (attacker != last_attacker) || (means_of_death != last_means_of_death); // update state information last_framenum = level.framenum; last_self = self; last_attacker = attacker; last_means_of_death = means_of_death; // the gauntlet only "fires" when it actually hits something if( (attacker != ENTITYNUM_WORLD) && (attacker != self) && (means_of_death == MOD_GAUNTLET) && (g_entities[attacker].client) ) { trap_RankReportInt( attacker, -1, QGR_KEY_SHOT_FIRED_GAUNTLET, 1, 1 ); } // don't track hazard damage, just deaths switch( means_of_death ) { case MOD_WATER: case MOD_SLIME: case MOD_LAVA: case MOD_CRUSH: case MOD_TELEFRAG: case MOD_FALLING: case MOD_SUICIDE: case MOD_TRIGGER_HURT: return; default: break; } // get splash damage switch( means_of_death ) { case MOD_GRENADE_SPLASH: case MOD_ROCKET_SPLASH: case MOD_PLASMA_SPLASH: case MOD_BFG_SPLASH: splash = damage; break; default: splash = 0; key_splash = -1; break; } // hit, damage, and splash taken switch( means_of_death ) { case MOD_GAUNTLET: key_hit = QGR_KEY_HIT_TAKEN_GAUNTLET; key_damage = QGR_KEY_DAMAGE_TAKEN_GAUNTLET; break; case MOD_MACHINEGUN: key_hit = QGR_KEY_HIT_TAKEN_MACHINEGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_MACHINEGUN; break; case MOD_SHOTGUN: key_hit = QGR_KEY_HIT_TAKEN_SHOTGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_SHOTGUN; break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_GRENADE; key_damage = QGR_KEY_DAMAGE_TAKEN_GRENADE; key_splash = QGR_KEY_SPLASH_TAKEN_GRENADE; break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_ROCKET; key_damage = QGR_KEY_DAMAGE_TAKEN_ROCKET; key_splash = QGR_KEY_SPLASH_TAKEN_ROCKET; break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_PLASMA; key_damage = QGR_KEY_DAMAGE_TAKEN_PLASMA; key_splash = QGR_KEY_SPLASH_TAKEN_PLASMA; break; case MOD_RAILGUN: key_hit = QGR_KEY_HIT_TAKEN_RAILGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_RAILGUN; break; case MOD_LIGHTNING: key_hit = QGR_KEY_HIT_TAKEN_LIGHTNING; key_damage = QGR_KEY_DAMAGE_TAKEN_LIGHTNING; break; case MOD_BFG: case MOD_BFG_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_BFG; key_damage = QGR_KEY_DAMAGE_TAKEN_BFG; key_splash = QGR_KEY_SPLASH_TAKEN_BFG; break; case MOD_GRAPPLE: key_hit = QGR_KEY_HIT_TAKEN_GRAPPLE; key_damage = QGR_KEY_DAMAGE_TAKEN_GRAPPLE; break; default: key_hit = QGR_KEY_HIT_TAKEN_UNKNOWN; key_damage = QGR_KEY_DAMAGE_TAKEN_UNKNOWN; break; } // report general and specific hit taken if( new_hit ) { trap_RankReportInt( self, -1, QGR_KEY_HIT_TAKEN, 1, 1 ); trap_RankReportInt( self, -1, key_hit, 1, 1 ); } // report general and specific damage taken trap_RankReportInt( self, -1, QGR_KEY_DAMAGE_TAKEN, damage, 1 ); trap_RankReportInt( self, -1, key_damage, damage, 1 ); // report general and specific splash taken if( splash != 0 ) { trap_RankReportInt( self, -1, QGR_KEY_SPLASH_TAKEN, splash, 1 ); trap_RankReportInt( self, -1, key_splash, splash, 1 ); } // hit, damage, and splash given if( (attacker != ENTITYNUM_WORLD) && (attacker != self) ) { switch( means_of_death ) { case MOD_GAUNTLET: key_hit = QGR_KEY_HIT_GIVEN_GAUNTLET; key_damage = QGR_KEY_DAMAGE_GIVEN_GAUNTLET; break; case MOD_MACHINEGUN: key_hit = QGR_KEY_HIT_GIVEN_MACHINEGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_MACHINEGUN; break; case MOD_SHOTGUN: key_hit = QGR_KEY_HIT_GIVEN_SHOTGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_SHOTGUN; break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_GRENADE; key_damage = QGR_KEY_DAMAGE_GIVEN_GRENADE; key_splash = QGR_KEY_SPLASH_GIVEN_GRENADE; break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_ROCKET; key_damage = QGR_KEY_DAMAGE_GIVEN_ROCKET; key_splash = QGR_KEY_SPLASH_GIVEN_ROCKET; break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_PLASMA; key_damage = QGR_KEY_DAMAGE_GIVEN_PLASMA; key_splash = QGR_KEY_SPLASH_GIVEN_PLASMA; break; case MOD_RAILGUN: key_hit = QGR_KEY_HIT_GIVEN_RAILGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_RAILGUN; break; case MOD_LIGHTNING: key_hit = QGR_KEY_HIT_GIVEN_LIGHTNING; key_damage = QGR_KEY_DAMAGE_GIVEN_LIGHTNING; break; case MOD_BFG: case MOD_BFG_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_BFG; key_damage = QGR_KEY_DAMAGE_GIVEN_BFG; key_splash = QGR_KEY_SPLASH_GIVEN_BFG; break; case MOD_GRAPPLE: key_hit = QGR_KEY_HIT_GIVEN_GRAPPLE; key_damage = QGR_KEY_DAMAGE_GIVEN_GRAPPLE; break; default: key_hit = QGR_KEY_HIT_GIVEN_UNKNOWN; key_damage = QGR_KEY_DAMAGE_GIVEN_UNKNOWN; break; } // report general and specific hit given // jwu 8/26/00 // had a case where attacker is 245 which is grnadeshooter attacker is // g_entities index not necessarilly clientnum if (g_entities[attacker].client) { if( new_hit ) { trap_RankReportInt( attacker, -1, QGR_KEY_HIT_GIVEN, 1, 1 ); trap_RankReportInt( attacker, -1, key_hit, 1, 1 ); } // report general and specific damage given trap_RankReportInt( attacker, -1, QGR_KEY_DAMAGE_GIVEN, damage, 1 ); trap_RankReportInt( attacker, -1, key_damage, damage, 1 ); // report general and specific splash given if( splash != 0 ) { trap_RankReportInt( attacker, -1, QGR_KEY_SPLASH_GIVEN, splash, 1 ); trap_RankReportInt( attacker, -1, key_splash, splash, 1 ); } } } // friendly fire if( (attacker != self) && OnSameTeam( &(g_entities[self]), &(g_entities[attacker])) && (g_entities[attacker].client) ) { // report teammate hit if( new_hit ) { trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_HIT_TAKEN, 1, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_HIT_GIVEN, 1, 1 ); } // report teammate damage trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_DAMAGE_TAKEN, damage, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_DAMAGE_GIVEN, damage, 1 ); // report teammate splash if( splash != 0 ) { trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_SPLASH_TAKEN, splash, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_SPLASH_GIVEN, splash, 1 ); } } } /* ================ G_RankPlayerDie ================ */ void G_RankPlayerDie( int self, int attacker, int means_of_death ) { int p1; int p2; if( level.warmupTime != 0 ) { // no reports during warmup period return; } if( attacker == ENTITYNUM_WORLD ) { p1 = self; p2 = -1; trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_DEATH, 1, 1 ); switch( means_of_death ) { case MOD_WATER: trap_RankReportInt( p1, p2, QGR_KEY_WATER, 1, 1 ); break; case MOD_SLIME: trap_RankReportInt( p1, p2, QGR_KEY_SLIME, 1, 1 ); break; case MOD_LAVA: trap_RankReportInt( p1, p2, QGR_KEY_LAVA, 1, 1 ); break; case MOD_CRUSH: trap_RankReportInt( p1, p2, QGR_KEY_CRUSH, 1, 1 ); break; case MOD_TELEFRAG: trap_RankReportInt( p1, p2, QGR_KEY_TELEFRAG, 1, 1 ); break; case MOD_FALLING: trap_RankReportInt( p1, p2, QGR_KEY_FALLING, 1, 1 ); break; case MOD_SUICIDE: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_CMD, 1, 1 ); break; case MOD_TRIGGER_HURT: trap_RankReportInt( p1, p2, QGR_KEY_TRIGGER_HURT, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_MISC, 1, 1 ); break; } } else if( attacker == self ) { p1 = self; p2 = -1; trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE, 1, 1 ); switch( means_of_death ) { case MOD_GAUNTLET: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GAUNTLET, 1, 1 ); break; case MOD_MACHINEGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_MACHINEGUN, 1, 1 ); break; case MOD_SHOTGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_SHOTGUN, 1, 1 ); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRENADE, 1, 1 ); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_ROCKET, 1, 1 ); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_PLASMA, 1, 1 ); break; case MOD_RAILGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_RAILGUN, 1, 1 ); break; case MOD_LIGHTNING: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_LIGHTNING, 1, 1 ); break; case MOD_BFG: case MOD_BFG_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_BFG, 1, 1 ); break; case MOD_GRAPPLE: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRAPPLE, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_UNKNOWN, 1, 1 ); break; } } else { p1 = attacker; p2 = self; trap_RankReportInt( p1, p2, QGR_KEY_FRAG, 1, 1 ); switch( means_of_death ) { case MOD_GAUNTLET: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GAUNTLET, 1, 1 ); break; case MOD_MACHINEGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_MACHINEGUN, 1, 1 ); break; case MOD_SHOTGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_SHOTGUN, 1, 1 ); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRENADE, 1, 1 ); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_ROCKET, 1, 1 ); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_PLASMA, 1, 1 ); break; case MOD_RAILGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_RAILGUN, 1, 1 ); break; case MOD_LIGHTNING: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_LIGHTNING, 1, 1 ); break; case MOD_BFG: case MOD_BFG_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_BFG, 1, 1 ); break; case MOD_GRAPPLE: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRAPPLE, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_UNKNOWN, 1, 1 ); break; } } } /* ================ G_RankWeaponTime ================ */ void G_RankWeaponTime( int self, int weapon ) { gclient_t* client; int time; if( level.warmupTime != 0 ) { // no reports during warmup period return; } client = g_entities[self].client; time = (level.time - client->weapon_change_time) / 1000; client->weapon_change_time = level.time; if( time <= 0 ) { return; } trap_RankReportInt( self, -1, QGR_KEY_TIME, time, 1 ); switch( weapon ) { case WP_GAUNTLET: trap_RankReportInt( self, -1, QGR_KEY_TIME_GAUNTLET, time, 1 ); break; case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_MACHINEGUN, time, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_SHOTGUN, time, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_TIME_GRENADE, time, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_TIME_ROCKET, time, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_TIME_LIGHTNING, time, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_RAILGUN, time, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_PLASMA, time, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_TIME_BFG, time, 1 ); break; case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_TIME_GRAPPLE, time, 1 ); break; default: break; } } /* ================ G_RankPickupWeapon ================ */ void G_RankPickupWeapon( int self, int weapon ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_PICKUP_WEAPON, 1, 1 ); switch( weapon ) { case WP_GAUNTLET: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GAUNTLET, 1, 1 ); break; case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_MACHINEGUN, 1, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_SHOTGUN, 1, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRENADE, 1, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_ROCKET, 1, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_LIGHTNING, 1, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_RAILGUN, 1, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_PLASMA, 1, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_BFG, 1, 1 ); break; case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRAPPLE, 1, 1 ); break; default: break; } } /* ================ G_RankPickupAmmo ================ */ void G_RankPickupAmmo( int self, int weapon, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_BOXES, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS, quantity, 1 ); switch( weapon ) { case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_BULLETS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BULLETS, quantity, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_SHELLS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SHELLS, quantity, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_BOXES_GRENADES, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_GRENADES, quantity, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_BOXES_ROCKETS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_ROCKETS, quantity, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_BOXES_LG_AMMO, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_LG_AMMO, quantity, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_SLUGS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SLUGS, quantity, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_CELLS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_CELLS, quantity, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_BOXES_BFG_AMMO, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BFG_AMMO, quantity, 1 ); break; default: break; } } /* ================ G_RankPickupHealth ================ */ void G_RankPickupHealth( int self, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_HEALTH, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_HEALTH_TOTAL, quantity, 1 ); switch( quantity ) { case 5: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_5, 1, 1 ); break; case 25: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_25, 1, 1 ); break; case 50: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_50, 1, 1 ); break; case 100: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_MEGA, 1, 1 ); break; default: break; } } /* ================ G_RankPickupArmor ================ */ void G_RankPickupArmor( int self, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_ARMOR, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ARMOR_TOTAL, quantity, 1 ); switch( quantity ) { case 5: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_SHARD, 1, 1 ); break; case 50: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_YELLOW, 1, 1 ); break; case 100: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_RED, 1, 1 ); break; default: break; } } /* ================ G_RankPickupPowerup ================ */ void G_RankPickupPowerup( int self, int powerup ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } // ctf flags are treated as powerups if( (powerup == PW_REDFLAG) || (powerup == PW_BLUEFLAG) ) { trap_RankReportInt( self, -1, QGR_KEY_FLAG_PICKUP, 1, 1 ); return; } trap_RankReportInt( self, -1, QGR_KEY_POWERUP, 1, 1 ); switch( powerup ) { case PW_QUAD: trap_RankReportInt( self, -1, QGR_KEY_QUAD, 1, 1 ); break; case PW_BATTLESUIT: trap_RankReportInt( self, -1, QGR_KEY_SUIT, 1, 1 ); break; case PW_HASTE: trap_RankReportInt( self, -1, QGR_KEY_HASTE, 1, 1 ); break; case PW_INVIS: trap_RankReportInt( self, -1, QGR_KEY_INVIS, 1, 1 ); break; case PW_REGEN: trap_RankReportInt( self, -1, QGR_KEY_REGEN, 1, 1 ); break; case PW_FLIGHT: trap_RankReportInt( self, -1, QGR_KEY_FLIGHT, 1, 1 ); break; default: break; } } /* ================ G_RankPickupHoldable ================ */ void G_RankPickupHoldable( int self, int holdable ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( holdable ) { case HI_MEDKIT: trap_RankReportInt( self, -1, QGR_KEY_MEDKIT, 1, 1 ); break; case HI_TELEPORTER: trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER, 1, 1 ); break; default: break; } } /* ================ G_RankUseHoldable ================ */ void G_RankUseHoldable( int self, int holdable ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( holdable ) { case HI_MEDKIT: trap_RankReportInt( self, -1, QGR_KEY_MEDKIT_USE, 1, 1 ); break; case HI_TELEPORTER: trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER_USE, 1, 1 ); break; default: break; } } /* ================ G_RankReward ================ */ void G_RankReward( int self, int award ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( award ) { case EF_AWARD_IMPRESSIVE: trap_RankReportInt( self, -1, QGR_KEY_IMPRESSIVE, 1, 1 ); break; case EF_AWARD_EXCELLENT: trap_RankReportInt( self, -1, QGR_KEY_EXCELLENT, 1, 1 ); break; default: break; } } /* ================ G_RankCapture ================ */ void G_RankCapture( int self ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_FLAG_CAPTURE, 1, 1 ); } /* ================ G_RankUserTeamName ================ */ void G_RankUserTeamName( int self, char* team_name ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportStr( self, -1, QGR_KEY_TEAM_NAME, team_name ); } /* ================ G_RankClientDisconnect ================ */ void G_RankClientDisconnect( int self ) { gclient_t* client; int time; int match_rating; if( level.warmupTime != 0 ) { // no reports during warmup period return; } // match rating client = g_entities[self].client; time = (level.time - client->pers.enterTime) / 1000; if( time < 60 ) { match_rating = 0; } else { match_rating = client->ps.persistant[PERS_MATCH_RATING] / time; } trap_RankReportInt( self, -1, QGR_KEY_MATCH_RATING, match_rating, 0 ); } /* ================ G_RankGameOver ================ */ void G_RankGameOver( void ) { int i; char str[MAX_INFO_VALUE]; int num; if( level.warmupTime != 0 ) { // no reports during warmup period return; } for( i = 0; i < level.maxclients; i++ ) { if( trap_RankUserStatus( i ) == QGR_STATUS_ACTIVE ) { G_RankClientDisconnect( i ); } } // hostname trap_Cvar_VariableStringBuffer( "sv_hostname", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_HOSTNAME, str ); // map trap_Cvar_VariableStringBuffer( "mapname", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_MAP, str ); // mod trap_Cvar_VariableStringBuffer( "fs_game", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_MOD, str ); // gametype num = trap_Cvar_VariableIntegerValue("g_gametype"); trap_RankReportInt( -1, -1, QGR_KEY_GAMETYPE, num, 0 ); // fraglimit num = trap_Cvar_VariableIntegerValue("fraglimit"); trap_RankReportInt( -1, -1, QGR_KEY_FRAGLIMIT, num, 0 ); // timelimit num = trap_Cvar_VariableIntegerValue("timelimit"); trap_RankReportInt( -1, -1, QGR_KEY_TIMELIMIT, num, 0 ); // maxclients num = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_RankReportInt( -1, -1, QGR_KEY_MAXCLIENTS, num, 0 ); // maxrate num = trap_Cvar_VariableIntegerValue("sv_maxRate"); trap_RankReportInt( -1, -1, QGR_KEY_MAXRATE, num, 0 ); // minping num = trap_Cvar_VariableIntegerValue("sv_minPing"); trap_RankReportInt( -1, -1, QGR_KEY_MINPING, num, 0 ); // maxping num = trap_Cvar_VariableIntegerValue("sv_maxPing"); trap_RankReportInt( -1, -1, QGR_KEY_MAXPING, num, 0 ); // dedicated num = trap_Cvar_VariableIntegerValue("dedicated"); trap_RankReportInt( -1, -1, QGR_KEY_DEDICATED, num, 0 ); // version trap_Cvar_VariableStringBuffer( "version", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_VERSION, str ); } openarena_0.8.8.orig/code/game/ai_dmq3.c0000644000175000017500000050017411656310264016541 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmq3.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_dmq3.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "../../ui/menudef.h" // sos001205 - for q3_ui also // from aasfile.h #define AREACONTENTS_MOVER 1024 #define AREACONTENTS_MODELNUMSHIFT 24 #define AREACONTENTS_MAXMODELNUM 0xFF #define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) #define IDEAL_ATTACKDIST 140 #define MAX_WAYPOINTS 128 #define MAX_EPAIRKEY 128 // bot_waypoint_t botai_waypoints[MAX_WAYPOINTS]; bot_waypoint_t *botai_freewaypoints; //NOTE: not using a cvars which can be updated because the game should be reloaded anyway int gametype; //game type int maxclients; //maximum number of clients vmCvar_t bot_grapple; vmCvar_t bot_rocketjump; vmCvar_t bot_fastchat; vmCvar_t bot_nochat; vmCvar_t bot_testrchat; vmCvar_t bot_challenge; vmCvar_t bot_predictobstacles; vmCvar_t g_spSkill; extern vmCvar_t bot_developer; vec3_t lastteleport_origin; //last teleport event origin float lastteleport_time; //last teleport event time int max_bspmodelindex; //maximum BSP model index //CTF flag goals bot_goal_t ctf_redflag; bot_goal_t ctf_blueflag; //Domination goals: bot_goal_t dom_points_bot[MAX_DOMINATION_POINTS]; bot_goal_t ctf_neutralflag; bot_goal_t redobelisk; bot_goal_t blueobelisk; bot_goal_t neutralobelisk; #define MAX_ALTROUTEGOALS 32 int altroutegoals_setup; aas_altroutegoal_t red_altroutegoals[MAX_ALTROUTEGOALS]; int red_numaltroutegoals; aas_altroutegoal_t blue_altroutegoals[MAX_ALTROUTEGOALS]; int blue_numaltroutegoals; /* ================== untrap_BotGetLevelItemGoal *same as trap_BotGetLevelItemGoal, but respects the gametype flag! ================== */ int untrap_BotGetLevelItemGoal(int start, char *classname, void /* struct bot_goal_s */ *goal) { static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "elimination", "ctf", "lms", "dd", "dom"}; char allowedGametypes[MAX_EPAIRKEY]; char *gametypeName; start = trap_BotGetLevelItemGoal(start,classname,goal); while(start>-1) { if(!trap_AAS_ValueForBSPEpairKey(start,"gametype",allowedGametypes,MAX_EPAIRKEY)) return start; //No gametype flag if( gametype >= GT_FFA && gametype < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[gametype]; if(strstr( allowedGametypes, gametypeName )) { //In gametype strig return start; } } else return start; start = trap_BotGetLevelItemGoal(start,classname,goal); } return -1; } /* ================== BotSetUserInfo ================== */ void BotSetUserInfo(bot_state_t *bs, char *key, char *value) { char userinfo[MAX_INFO_STRING]; trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, key, value); trap_SetUserinfo(bs->client, userinfo); ClientUserinfoChanged( bs->client ); } /* ================== BotCTFCarryingFlag ================== */ int BotCTFCarryingFlag(bot_state_t *bs) { if (gametype != GT_CTF && gametype!=GT_CTF_ELIMINATION) return CTF_FLAG_NONE; if (bs->inventory[INVENTORY_REDFLAG] > 0) return CTF_FLAG_RED; else if (bs->inventory[INVENTORY_BLUEFLAG] > 0) return CTF_FLAG_BLUE; return CTF_FLAG_NONE; } /* ================== BotTeam ================== */ int BotTeam(bot_state_t *bs) { if (bs->client < 0 || bs->client >= MAX_CLIENTS) { return qfalse; } if (level.clients[bs->client].sess.sessionTeam == TEAM_RED) { return TEAM_RED; } else if (level.clients[bs->client].sess.sessionTeam == TEAM_BLUE) { return TEAM_BLUE; } return TEAM_FREE; } /* ================== BotOppositeTeam ================== */ int BotOppositeTeam(bot_state_t *bs) { switch(BotTeam(bs)) { case TEAM_RED: return TEAM_BLUE; case TEAM_BLUE: return TEAM_RED; default: return TEAM_FREE; } } /* ================== BotEnemyFlag ================== */ bot_goal_t *BotEnemyFlag(bot_state_t *bs) { if (BotTeam(bs) == TEAM_RED) { return &ctf_blueflag; } else { return &ctf_redflag; } } /* ================== BotTeamFlag ================== */ bot_goal_t *BotTeamFlag(bot_state_t *bs) { if (BotTeam(bs) == TEAM_RED) { return &ctf_redflag; } else { return &ctf_blueflag; } } /* ================== EntityIsDead ================== */ qboolean EntityIsDead(aas_entityinfo_t *entinfo) { playerState_t ps; if (entinfo->number >= 0 && entinfo->number < MAX_CLIENTS) { //retrieve the current client state BotAI_GetClientState( entinfo->number, &ps ); if (ps.pm_type != PM_NORMAL) return qtrue; } return qfalse; } /* ================== EntityCarriesFlag ================== */ qboolean EntityCarriesFlag(aas_entityinfo_t *entinfo) { if ( entinfo->powerups & ( 1 << PW_REDFLAG ) ) return qtrue; if ( entinfo->powerups & ( 1 << PW_BLUEFLAG ) ) return qtrue; if ( entinfo->powerups & ( 1 << PW_NEUTRALFLAG ) ) return qtrue; return qfalse; } /* ================== EntityIsInvisible ================== */ qboolean EntityIsInvisible(aas_entityinfo_t *entinfo) { // the flag is always visible if (EntityCarriesFlag(entinfo)) { return qfalse; } if (entinfo->powerups & (1 << PW_INVIS)) { return qtrue; } return qfalse; } /* ================== EntityIsShooting ================== */ qboolean EntityIsShooting(aas_entityinfo_t *entinfo) { if (entinfo->flags & EF_FIRING) { return qtrue; } return qfalse; } /* ================== EntityIsChatting ================== */ qboolean EntityIsChatting(aas_entityinfo_t *entinfo) { if (entinfo->flags & EF_TALK) { return qtrue; } return qfalse; } /* ================== EntityHasQuad ================== */ qboolean EntityHasQuad(aas_entityinfo_t *entinfo) { if (entinfo->powerups & (1 << PW_QUAD)) { return qtrue; } return qfalse; } /* ================== EntityHasKamikze ================== */ qboolean EntityHasKamikaze(aas_entityinfo_t *entinfo) { if (entinfo->flags & EF_KAMIKAZE) { return qtrue; } return qfalse; } /* ================== EntityCarriesCubes ================== */ qboolean EntityCarriesCubes(aas_entityinfo_t *entinfo) { entityState_t state; if (gametype != GT_HARVESTER) return qfalse; //FIXME: get this info from the aas_entityinfo_t ? BotAI_GetEntityState(entinfo->number, &state); if (state.generic1 > 0) return qtrue; return qfalse; } /* ================== Bot1FCTFCarryingFlag ================== */ int Bot1FCTFCarryingFlag(bot_state_t *bs) { if (gametype != GT_1FCTF) return qfalse; if (bs->inventory[INVENTORY_NEUTRALFLAG] > 0) return qtrue; return qfalse; } /* ================== BotHarvesterCarryingCubes ================== */ int BotHarvesterCarryingCubes(bot_state_t *bs) { if (gametype != GT_HARVESTER) return qfalse; if (bs->inventory[INVENTORY_REDCUBE] > 0) return qtrue; if (bs->inventory[INVENTORY_BLUECUBE] > 0) return qtrue; return qfalse; } //#endif /* ================== BotRememberLastOrderedTask ================== */ void BotRememberLastOrderedTask(bot_state_t *bs) { if (!bs->ordered) { return; } bs->lastgoal_decisionmaker = bs->decisionmaker; bs->lastgoal_ltgtype = bs->ltgtype; memcpy(&bs->lastgoal_teamgoal, &bs->teamgoal, sizeof(bot_goal_t)); bs->lastgoal_teammate = bs->teammate; } /* ================== BotSetTeamStatus ================== */ void BotSetTeamStatus(bot_state_t *bs) { int teamtask; aas_entityinfo_t entinfo; teamtask = TEAMTASK_PATROL; switch(bs->ltgtype) { case LTG_TEAMHELP: break; case LTG_TEAMACCOMPANY: BotEntityInfo(bs->teammate, &entinfo); if ( ( (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION || gametype == GT_1FCTF) && EntityCarriesFlag(&entinfo)) || ( gametype == GT_HARVESTER && EntityCarriesCubes(&entinfo)) ) { teamtask = TEAMTASK_ESCORT; } else { teamtask = TEAMTASK_FOLLOW; } break; case LTG_DEFENDKEYAREA: teamtask = TEAMTASK_DEFENSE; break; case LTG_GETFLAG: teamtask = TEAMTASK_OFFENSE; break; case LTG_RUSHBASE: teamtask = TEAMTASK_DEFENSE; break; case LTG_RETURNFLAG: teamtask = TEAMTASK_RETRIEVE; break; case LTG_CAMP: case LTG_CAMPORDER: teamtask = TEAMTASK_CAMP; break; case LTG_PATROL: teamtask = TEAMTASK_PATROL; break; case LTG_GETITEM: teamtask = TEAMTASK_PATROL; break; case LTG_KILL: teamtask = TEAMTASK_PATROL; break; case LTG_HARVEST: teamtask = TEAMTASK_OFFENSE; break; case LTG_ATTACKENEMYBASE: teamtask = TEAMTASK_OFFENSE; break; case LTG_POINTA: if(BotTeam(bs) == TEAM_BLUE) teamtask = TEAMTASK_OFFENSE; else teamtask = TEAMTASK_DEFENSE; case LTG_POINTB: if(BotTeam(bs) == TEAM_RED) teamtask = TEAMTASK_OFFENSE; else teamtask = TEAMTASK_DEFENSE; default: teamtask = TEAMTASK_PATROL; break; } BotSetUserInfo(bs, "teamtask", va("%d", teamtask)); } /* ================== BotSetLastOrderedTask ================== */ int BotSetLastOrderedTask(bot_state_t *bs) { if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { // don't go back to returning the flag if it's at the base if ( bs->lastgoal_ltgtype == LTG_RETURNFLAG ) { if ( BotTeam(bs) == TEAM_RED ) { if ( bs->redflagstatus == 0 ) { bs->lastgoal_ltgtype = 0; } } else { if ( bs->blueflagstatus == 0 ) { bs->lastgoal_ltgtype = 0; } } } } if ( bs->lastgoal_ltgtype ) { bs->decisionmaker = bs->lastgoal_decisionmaker; bs->ordered = qtrue; bs->ltgtype = bs->lastgoal_ltgtype; memcpy(&bs->teamgoal, &bs->lastgoal_teamgoal, sizeof(bot_goal_t)); bs->teammate = bs->lastgoal_teammate; bs->teamgoal_time = FloatTime() + 300; BotSetTeamStatus(bs); // if ( gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if ( bs->ltgtype == LTG_GETFLAG ) { bot_goal_t *tb, *eb; int tt, et; tb = BotTeamFlag(bs); eb = BotEnemyFlag(bs); tt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, tb->areanum, TFL_DEFAULT); et = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, eb->areanum, TFL_DEFAULT); // if the travel time towards the enemy base is larger than towards our base if (et > tt) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } } } return qtrue; } return qfalse; } /* ================== BotRefuseOrder ================== */ void BotRefuseOrder(bot_state_t *bs) { if (!bs->ordered) return; // if the bot was ordered to do something if ( bs->order_time && bs->order_time > FloatTime() - 10 ) { trap_EA_Action(bs->client, ACTION_NEGATIVE); BotVoiceChat(bs, bs->decisionmaker, VOICECHAT_NO); bs->order_time = 0; } } /* ================== BotCTFSeekGoals ================== */ void BotCTFSeekGoals(bot_state_t *bs) { float rnd, l1, l2; int flagstatus, c; vec3_t dir; aas_entityinfo_t entinfo; //when carrying a flag in ctf the bot should rush to the base if (BotCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; // switch(BotTeam(bs)) { case TEAM_RED: VectorSubtract(bs->origin, ctf_blueflag.origin, dir); break; case TEAM_BLUE: VectorSubtract(bs->origin, ctf_redflag.origin, dir); break; default: VectorSet(dir, 999, 999, 999); break; } // if the bot picked up the flag very close to the enemy base if ( VectorLength(dir) < 128 ) { // get an alternative route goal through the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } else { // don't use any alt route goal, just get the hell out of the base bs->altroutegoal.areanum = 0; } BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_OFFENSE)); BotVoiceChat(bs, -1, VOICECHAT_IHAVEFLAG); } else if (bs->rushbaseaway_time > FloatTime()) { if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus; else flagstatus = bs->blueflagstatus; //if the flag is back if (flagstatus == 0) { bs->rushbaseaway_time = 0; } } return; } // if the bot decided to follow someone if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { // if the team mate being accompanied no longer carries the flag BotEntityInfo(bs->teammate, &entinfo); if (!EntityCarriesFlag(&entinfo)) { bs->ltgtype = 0; } } // if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; //if our team has the enemy flag and our flag is at the base if (flagstatus == 1) { // if (bs->owndecision_time < FloatTime()) { //if Not defending the base already if (!(bs->ltgtype == LTG_DEFENDKEYAREA && (bs->teamgoal.number == ctf_redflag.number || bs->teamgoal.number == ctf_blueflag.number))) { //if there is a visible team mate flag carrier c = BotTeamFlagCarrierVisible(bs); if (c >= 0 && // and not already following the team mate flag carrier (bs->ltgtype != LTG_TEAMACCOMPANY || bs->teammate != c)) { // BotRefuseOrder(bs); //follow the flag carrier bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } } return; } //if the enemy has our flag else if (flagstatus == 2) { // if (bs->owndecision_time < FloatTime()) { //if enemy flag carrier is visible c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) { //FIXME: fight enemy flag carrier } //if not already doing something important if (bs->ltgtype != LTG_GETFLAG && bs->ltgtype != LTG_RETURNFLAG && bs->ltgtype != LTG_TEAMHELP && bs->ltgtype != LTG_TEAMACCOMPANY && bs->ltgtype != LTG_CAMPORDER && bs->ltgtype != LTG_PATROL && bs->ltgtype != LTG_GETITEM) { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (random() < 0.5) { //go for the enemy flag bs->ltgtype = LTG_GETFLAG; } else { bs->ltgtype = LTG_RETURNFLAG; } //no team message bs->teammessage_time = 0; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } return; } //if both flags Not at their bases else if (flagstatus == 3) { // if (bs->owndecision_time < FloatTime()) { // if not trying to return the flag and not following the team flag carrier if ( bs->ltgtype != LTG_RETURNFLAG && bs->ltgtype != LTG_TEAMACCOMPANY ) { // c = BotTeamFlagCarrierVisible(bs); // if there is a visible team mate flag carrier if (c >= 0) { BotRefuseOrder(bs); //follow the flag carrier bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } else { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; //get the enemy flag bs->teammessage_time = FloatTime() + 2 * random(); //get the flag bs->ltgtype = LTG_RETURNFLAG; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } } return; } // don't just do something wait for the bot team leader to give orders if (BotTeamLeader(bs)) { return; } // if the bot is ordered to do something if ( bs->lastgoal_ltgtype ) { bs->teamgoal_time += 60; } // if the bot decided to do something on it's own and has a last ordered goal if ( !bs->ordered && bs->lastgoal_ltgtype ) { bs->ltgtype = 0; } //if already a CTF or team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_RETURNFLAG || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } // if (BotSetLastOrderedTask(bs)) return; // if (bs->owndecision_time > FloatTime()) return;; //if the bot is roaming if (bs->ctfroam_time > FloatTime()) return; //if the bot has anough aggression to decide what to do if (BotAggression(bs) < 50) return; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); // if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { if (bs->teamtaskpreference & TEAMTP_ATTACKER) { l1 = 0.7f; } else { l1 = 0.2f; } l2 = 0.9f; } else { l1 = 0.4f; l2 = 0.7f; } //get the flag or defend the base rnd = random(); if (rnd < l1 && ctf_redflag.areanum && ctf_blueflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; bs->ltgtype = LTG_GETFLAG; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); BotSetTeamStatus(bs); } else if (rnd < l2 && ctf_redflag.areanum && ctf_blueflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); } else { bs->ltgtype = 0; //set the time the bot will stop roaming bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; BotSetTeamStatus(bs); } bs->owndecision_time = FloatTime() + 5; #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotCTFRetreatGoals ================== */ void BotCTFRetreatGoals(bot_state_t *bs) { //when carrying a flag in ctf the bot should rush to the base if (BotCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; BotSetTeamStatus(bs); } } } /* ================== BotDomSeekGoals ================== */ /*void BotDomSeekGoals(bot_state_t *bs) { int index; bs->ltgtype = LTG_DOMHOLD; //For debugging we are forcing roam index=0; //dom_points_bot[i] if(bs->ltgtype == LTG_DOMHOLD) { //index = 0; index = ((rand()) % (level.domination_points_count)); } //if(bs->ltgtype == LTG_DOMROAM) { //} memcpy(&bs->teamgoal, &dom_points_bot[index], sizeof(bot_goal_t)); BotAlternateRoute(bs, &bs->teamgoal); BotSetTeamStatus(bs); }*/ /* ================== BotDDSeekGoals ================== */ void BotDDSeekGoals(bot_state_t *bs) { /*if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_GETITEM) { return; }*/ if(bs->ltgtype == LTG_POINTA) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); if(bs->ltgtype == LTG_POINTB) memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); if(bs->ltgtype == LTG_POINTA || bs->ltgtype == LTG_POINTB) return; if(rand()%2==0) bs->ltgtype = LTG_POINTA; else bs->ltgtype = LTG_POINTB; if(bs->ltgtype == LTG_POINTA) { memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); if(BotTeam(bs) == TEAM_BLUE) BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_OFFENSE)); else BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_DEFENSE)); } else if(bs->ltgtype == LTG_POINTB) { memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); if(BotTeam(bs) == TEAM_RED) BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_OFFENSE)); else BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_DEFENSE)); } } /* ================== Bot1FCTFSeekGoals ================== */ void Bot1FCTFSeekGoals(bot_state_t *bs) { aas_entityinfo_t entinfo; float rnd, l1, l2; int c; //when carrying a flag in ctf the bot should rush to the base if (Bot1FCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); BotVoiceChat(bs, -1, VOICECHAT_IHAVEFLAG); } return; } // if the bot decided to follow someone if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { // if the team mate being accompanied no longer carries the flag BotEntityInfo(bs->teammate, &entinfo); if (!EntityCarriesFlag(&entinfo)) { bs->ltgtype = 0; } } //our team has the flag if (bs->neutralflagstatus == 1) { if (bs->owndecision_time < FloatTime()) { // if not already following someone if (bs->ltgtype != LTG_TEAMACCOMPANY) { //if there is a visible team mate flag carrier c = BotTeamFlagCarrierVisible(bs); if (c >= 0) { BotRefuseOrder(bs); //follow the flag carrier bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; return; } } //if already a CTF or team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_ATTACKENEMYBASE || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } //if not already attacking the enemy base if (bs->ltgtype != LTG_ATTACKENEMYBASE) { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } return; } //enemy team has the flag else if (bs->neutralflagstatus == 2) { if (bs->owndecision_time < FloatTime()) { c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) { //FIXME: attack enemy flag carrier } //if already a CTF or team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_GETITEM) { return; } // if not already defending the base if (bs->ltgtype != LTG_DEFENDKEYAREA) { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } return; } // don't just do something wait for the bot team leader to give orders if (BotTeamLeader(bs)) { return; } // if the bot is ordered to do something if ( bs->lastgoal_ltgtype ) { bs->teamgoal_time += 60; } // if the bot decided to do something on it's own and has a last ordered goal if ( !bs->ordered && bs->lastgoal_ltgtype ) { bs->ltgtype = 0; } //if already a CTF or team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_RETURNFLAG || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_ATTACKENEMYBASE || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } // if (BotSetLastOrderedTask(bs)) return; // if (bs->owndecision_time > FloatTime()) return;; //if the bot is roaming if (bs->ctfroam_time > FloatTime()) return; //if the bot has anough aggression to decide what to do if (BotAggression(bs) < 50) return; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); // if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { if (bs->teamtaskpreference & TEAMTP_ATTACKER) { l1 = 0.7f; } else { l1 = 0.2f; } l2 = 0.9f; } else { l1 = 0.4f; l2 = 0.7f; } //get the flag or defend the base rnd = random(); if (rnd < l1 && ctf_neutralflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; bs->ltgtype = LTG_GETFLAG; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; BotSetTeamStatus(bs); } else if (rnd < l2 && ctf_redflag.areanum && ctf_blueflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); } else { bs->ltgtype = 0; //set the time the bot will stop roaming bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; BotSetTeamStatus(bs); } bs->owndecision_time = FloatTime() + 5; #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== Bot1FCTFRetreatGoals ================== */ void Bot1FCTFRetreatGoals(bot_state_t *bs) { //when carrying a flag in ctf the bot should rush to the enemy base if (Bot1FCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); BotSetTeamStatus(bs); } } } /* ================== BotObeliskSeekGoals ================== */ void BotObeliskSeekGoals(bot_state_t *bs) { float rnd, l1, l2; // don't just do something wait for the bot team leader to give orders if (BotTeamLeader(bs)) { return; } // if the bot is ordered to do something if ( bs->lastgoal_ltgtype ) { bs->teamgoal_time += 60; } //if already a team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_RETURNFLAG || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_ATTACKENEMYBASE || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } // if (BotSetLastOrderedTask(bs)) return; //if the bot is roaming if (bs->ctfroam_time > FloatTime()) return; //if the bot has anough aggression to decide what to do if (BotAggression(bs) < 50) return; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); // if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { if (bs->teamtaskpreference & TEAMTP_ATTACKER) { l1 = 0.7f; } else { l1 = 0.2f; } l2 = 0.9f; } else { l1 = 0.4f; l2 = 0.7f; } //get the flag or defend the base rnd = random(); if (rnd < l1 && redobelisk.areanum && blueobelisk.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the time the bot will stop attacking the enemy base bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; //get an alternate route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); BotSetTeamStatus(bs); } else if (rnd < l2 && redobelisk.areanum && blueobelisk.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); } else { bs->ltgtype = 0; //set the time the bot will stop roaming bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; BotSetTeamStatus(bs); } } /* ================== BotGoHarvest ================== */ void BotGoHarvest(bot_state_t *bs) { // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_HARVEST; //set the time the bot will stop harvesting bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; bs->harvestaway_time = 0; BotSetTeamStatus(bs); } /* ================== BotObeliskRetreatGoals ================== */ void BotObeliskRetreatGoals(bot_state_t *bs) { //nothing special } /* ================== BotHarvesterSeekGoals ================== */ void BotHarvesterSeekGoals(bot_state_t *bs) { aas_entityinfo_t entinfo; float rnd, l1, l2; int c; //when carrying cubes in harvester the bot should rush to the base if (BotHarvesterCarryingCubes(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); } return; } // don't just do something wait for the bot team leader to give orders if (BotTeamLeader(bs)) { return; } // if the bot decided to follow someone if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { // if the team mate being accompanied no longer carries the flag BotEntityInfo(bs->teammate, &entinfo); if (!EntityCarriesCubes(&entinfo)) { bs->ltgtype = 0; } } // if the bot is ordered to do something if ( bs->lastgoal_ltgtype ) { bs->teamgoal_time += 60; } //if not yet doing something if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_ATTACKENEMYBASE || bs->ltgtype == LTG_HARVEST || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } // if (BotSetLastOrderedTask(bs)) return; //if the bot is roaming if (bs->ctfroam_time > FloatTime()) return; //if the bot has anough aggression to decide what to do if (BotAggression(bs) < 50) return; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); // c = BotEnemyCubeCarrierVisible(bs); if (c >= 0) { //FIXME: attack enemy cube carrier } if (bs->ltgtype != LTG_TEAMACCOMPANY) { //if there is a visible team mate carrying cubes c = BotTeamCubeCarrierVisible(bs); if (c >= 0) { //follow the team mate carrying cubes bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter BotSetTeamStatus(bs); return; } } // if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { if (bs->teamtaskpreference & TEAMTP_ATTACKER) { l1 = 0.7f; } else { l1 = 0.2f; } l2 = 0.9f; } else { l1 = 0.4f; l2 = 0.7f; } // rnd = random(); if (rnd < l1 && redobelisk.areanum && blueobelisk.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; BotGoHarvest(bs); } else if (rnd < l2 && redobelisk.areanum && blueobelisk.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); } else { bs->ltgtype = 0; //set the time the bot will stop roaming bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; BotSetTeamStatus(bs); } } /* ================== BotHarvesterRetreatGoals ================== */ void BotHarvesterRetreatGoals(bot_state_t *bs) { //when carrying cubes in harvester the bot should rush to the base if (BotHarvesterCarryingCubes(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; BotSetTeamStatus(bs); } return; } } //#endif /* ================== BotTeamGoals ================== */ void BotTeamGoals(bot_state_t *bs, int retreat) { if ( retreat ) { if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION ) { BotCTFRetreatGoals(bs); } else if (gametype == GT_1FCTF) { Bot1FCTFRetreatGoals(bs); } else if (gametype == GT_OBELISK) { BotObeliskRetreatGoals(bs); } else if (gametype == GT_HARVESTER) { BotHarvesterRetreatGoals(bs); } } else { if (gametype == GT_CTF|| gametype == GT_CTF_ELIMINATION) { //decide what to do in CTF mode BotCTFSeekGoals(bs); } else if (gametype == GT_1FCTF) { Bot1FCTFSeekGoals(bs); } else if (gametype == GT_OBELISK) { BotObeliskSeekGoals(bs); } else if (gametype == GT_HARVESTER) { BotHarvesterSeekGoals(bs); } } if(gametype == GT_DOUBLE_D) //Don't care about retreat BotDDSeekGoals(bs); //if(gametype == GT_DOMINATION) //Don't care about retreat // BotDomSeekGoals(bs); // reset the order time which is used to see if // we decided to refuse an order bs->order_time = 0; } /* ================== BotPointAreaNum ================== */ int BotPointAreaNum(vec3_t origin) { int areanum, numareas, areas[10]; vec3_t end; areanum = trap_AAS_PointAreaNum(origin); if (areanum) return areanum; VectorCopy(origin, end); end[2] += 10; numareas = trap_AAS_TraceAreas(origin, end, areas, NULL, 10); if (numareas > 0) return areas[0]; return 0; } /* ================== ClientName ================== */ char *ClientName(int client, char *name, int size) { char buf[MAX_INFO_STRING]; if (client < 0 || client >= MAX_CLIENTS) { BotAI_Print(PRT_ERROR, "ClientName: client out of range\n"); return "[client out of range]"; } trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); strncpy(name, Info_ValueForKey(buf, "n"), size-1); name[size-1] = '\0'; Q_CleanStr( name ); return name; } /* ================== ClientSkin ================== */ char *ClientSkin(int client, char *skin, int size) { char buf[MAX_INFO_STRING]; if (client < 0 || client >= MAX_CLIENTS) { BotAI_Print(PRT_ERROR, "ClientSkin: client out of range\n"); return "[client out of range]"; } trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); strncpy(skin, Info_ValueForKey(buf, "model"), size-1); skin[size-1] = '\0'; return skin; } /* ================== ClientFromName ================== */ int ClientFromName(char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; } return -1; } /* ================== ClientOnSameTeamFromName ================== */ int ClientOnSameTeamFromName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (!BotSameTeam(bs, i)) continue; trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; } return -1; } /* ================== stristr ================== */ char *stristr(char *str, char *charset) { int i; while(*str) { for (i = 0; charset[i] && str[i]; i++) { if (toupper(charset[i]) != toupper(str[i])) break; } if (!charset[i]) return str; str++; } return NULL; } /* ================== EasyClientName ================== */ char *EasyClientName(int client, char *buf, int size) { int i; char *str1, *str2, *ptr, c; char name[128]; ClientName(client, name, sizeof(name)); for (i = 0; name[i]; i++) name[i] &= 127; //remove all spaces for (ptr = strstr(name, " "); ptr; ptr = strstr(name, " ")) { memmove(ptr, ptr+1, strlen(ptr+1)+1); } //check for [x] and ]x[ clan names str1 = strstr(name, "["); str2 = strstr(name, "]"); if (str1 && str2) { if (str2 > str1) memmove(str1, str2+1, strlen(str2+1)+1); else memmove(str2, str1+1, strlen(str1+1)+1); } //remove Mr prefix if ((name[0] == 'm' || name[0] == 'M') && (name[1] == 'r' || name[1] == 'R')) { memmove(name, name+2, strlen(name+2)+1); } //only allow lower case alphabet characters ptr = name; while(*ptr) { c = *ptr; if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') { ptr++; } else if (c >= 'A' && c <= 'Z') { *ptr += 'a' - 'A'; ptr++; } else { memmove(ptr, ptr+1, strlen(ptr + 1)+1); } } strncpy(buf, name, size-1); buf[size-1] = '\0'; return buf; } /* ================== BotSynonymContext ================== */ int BotSynonymContext(bot_state_t *bs) { int context; context = CONTEXT_NORMAL|CONTEXT_NEARBYITEM|CONTEXT_NAMES; // if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION || gametype == GT_1FCTF ) { if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_CTFREDTEAM; else context |= CONTEXT_CTFBLUETEAM; } else if (gametype == GT_OBELISK) { if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_OBELISKREDTEAM; else context |= CONTEXT_OBELISKBLUETEAM; } else if (gametype == GT_HARVESTER) { if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_HARVESTERREDTEAM; else context |= CONTEXT_HARVESTERBLUETEAM; } return context; } /* ================== BotChooseWeapon ================== */ void BotChooseWeapon(bot_state_t *bs) { int newweaponnum; if (bs->cur_ps.weaponstate == WEAPON_RAISING || bs->cur_ps.weaponstate == WEAPON_DROPPING) { trap_EA_SelectWeapon(bs->client, bs->weaponnum); } else { if(g_instantgib.integer) newweaponnum = WP_RAILGUN; else if(g_rockets.integer) newweaponnum = WP_ROCKET_LAUNCHER; else newweaponnum = trap_BotChooseBestFightWeapon(bs->ws, bs->inventory); if (bs->weaponnum != newweaponnum) bs->weaponchange_time = FloatTime(); bs->weaponnum = newweaponnum; //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum); trap_EA_SelectWeapon(bs->client, bs->weaponnum); } } /* ================== BotSetupForMovement ================== */ void BotSetupForMovement(bot_state_t *bs) { bot_initmove_t initmove; memset(&initmove, 0, sizeof(bot_initmove_t)); VectorCopy(bs->cur_ps.origin, initmove.origin); VectorCopy(bs->cur_ps.velocity, initmove.velocity); VectorClear(initmove.viewoffset); initmove.viewoffset[2] += bs->cur_ps.viewheight; initmove.entitynum = bs->entitynum; initmove.client = bs->client; initmove.thinktime = bs->thinktime; //set the onground flag if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) initmove.or_moveflags |= MFL_ONGROUND; //set the teleported flag if ((bs->cur_ps.pm_flags & PMF_TIME_KNOCKBACK) && (bs->cur_ps.pm_time > 0)) { initmove.or_moveflags |= MFL_TELEPORTED; } //set the waterjump flag if ((bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP) && (bs->cur_ps.pm_time > 0)) { initmove.or_moveflags |= MFL_WATERJUMP; } //set presence type if (bs->cur_ps.pm_flags & PMF_DUCKED) initmove.presencetype = PRESENCE_CROUCH; else initmove.presencetype = PRESENCE_NORMAL; // if (bs->walker > 0.5) initmove.or_moveflags |= MFL_WALK; // VectorCopy(bs->viewangles, initmove.viewangles); // trap_BotInitMoveState(bs->ms, &initmove); } /* ================== BotCheckItemPickup ================== */ void BotCheckItemPickup(bot_state_t *bs, int *oldinventory) { int offence, leader; if (gametype <= GT_TEAM && g_ffa_gt==0) return; offence = -1; // go into offence if picked up the kamikaze or invulnerability if (!oldinventory[INVENTORY_KAMIKAZE] && bs->inventory[INVENTORY_KAMIKAZE] >= 1) { offence = qtrue; } if (!oldinventory[INVENTORY_INVULNERABILITY] && bs->inventory[INVENTORY_INVULNERABILITY] >= 1) { offence = qtrue; } // if not already wearing the kamikaze or invulnerability if (!bs->inventory[INVENTORY_KAMIKAZE] && !bs->inventory[INVENTORY_INVULNERABILITY]) { if (!oldinventory[INVENTORY_SCOUT] && bs->inventory[INVENTORY_SCOUT] >= 1) { offence = qtrue; } if (!oldinventory[INVENTORY_GUARD] && bs->inventory[INVENTORY_GUARD] >= 1) { offence = qtrue; } if (!oldinventory[INVENTORY_DOUBLER] && bs->inventory[INVENTORY_DOUBLER] >= 1) { offence = qfalse; } if (!oldinventory[INVENTORY_AMMOREGEN] && bs->inventory[INVENTORY_AMMOREGEN] >= 1) { offence = qfalse; } } if (offence >= 0) { leader = ClientFromName(bs->teamleader); if (offence) { if (!(bs->teamtaskpreference & TEAMTP_ATTACKER)) { // if we have a bot team leader if (BotTeamLeader(bs)) { // tell the leader we want to be on offence BotVoiceChat(bs, leader, VOICECHAT_WANTONOFFENSE); //BotAI_BotInitialChat(bs, "wantoffence", NULL); //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); } else if (g_spSkill.integer <= 3) { if ( ( bs->ltgtype != LTG_GETFLAG ) && ( bs->ltgtype != LTG_ATTACKENEMYBASE ) && ( bs->ltgtype != LTG_HARVEST ) && ( ( ( gametype != GT_CTF ) && ( gametype != GT_CTF_ELIMINATION ) ) || ( ( bs->redflagstatus == 0 ) && ( bs->blueflagstatus == 0 ) ) ) && ( ( gametype != GT_1FCTF ) || ( bs->neutralflagstatus == 0 ) ) ) { // tell the leader we want to be on offence BotVoiceChat(bs, leader, VOICECHAT_WANTONOFFENSE); //BotAI_BotInitialChat(bs, "wantoffence", NULL); //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); } bs->teamtaskpreference |= TEAMTP_ATTACKER; } } bs->teamtaskpreference &= ~TEAMTP_DEFENDER; } else { if (!(bs->teamtaskpreference & TEAMTP_DEFENDER)) { // if we have a bot team leader if (BotTeamLeader(bs)) { // tell the leader we want to be on defense BotVoiceChat(bs, -1, VOICECHAT_WANTONDEFENSE); //BotAI_BotInitialChat(bs, "wantdefence", NULL); //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); } else if ( (g_spSkill.integer <= 3) && ( bs->ltgtype != LTG_DEFENDKEYAREA ) && ( ( ( gametype != GT_CTF ) && ( gametype != GT_CTF_ELIMINATION ) ) || ( ( bs->redflagstatus == 0 ) && ( bs->blueflagstatus == 0 ) ) ) && ( ( gametype != GT_1FCTF ) || ( bs->neutralflagstatus == 0 ) ) ) { // tell the leader we want to be on defense BotVoiceChat(bs, -1, VOICECHAT_WANTONDEFENSE); //BotAI_BotInitialChat(bs, "wantdefence", NULL); //trap_BotEnterChat(bs->cs, leader, CHAT_TELL); } bs->teamtaskpreference |= TEAMTP_DEFENDER; } bs->teamtaskpreference &= ~TEAMTP_ATTACKER; } } //#endif } /* ================== BotUpdateInventory ================== */ void BotUpdateInventory(bot_state_t *bs) { int oldinventory[MAX_ITEMS]; memcpy(oldinventory, bs->inventory, sizeof(oldinventory)); //armor bs->inventory[INVENTORY_ARMOR] = bs->cur_ps.stats[STAT_ARMOR]; //weapons bs->inventory[INVENTORY_GAUNTLET] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GAUNTLET)) != 0; bs->inventory[INVENTORY_SHOTGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SHOTGUN)) != 0; bs->inventory[INVENTORY_MACHINEGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_MACHINEGUN)) != 0; bs->inventory[INVENTORY_GRENADELAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRENADE_LAUNCHER)) != 0; bs->inventory[INVENTORY_ROCKETLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_ROCKET_LAUNCHER)) != 0; bs->inventory[INVENTORY_LIGHTNING] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_LIGHTNING)) != 0; bs->inventory[INVENTORY_RAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_RAILGUN)) != 0; bs->inventory[INVENTORY_PLASMAGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PLASMAGUN)) != 0; bs->inventory[INVENTORY_BFG10K] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_BFG)) != 0; bs->inventory[INVENTORY_GRAPPLINGHOOK] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRAPPLING_HOOK)) != 0; bs->inventory[INVENTORY_NAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_NAILGUN)) != 0;; bs->inventory[INVENTORY_PROXLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PROX_LAUNCHER)) != 0;; bs->inventory[INVENTORY_CHAINGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_CHAINGUN)) != 0;; //ammo bs->inventory[INVENTORY_SHELLS] = bs->cur_ps.ammo[WP_SHOTGUN]; bs->inventory[INVENTORY_BULLETS] = bs->cur_ps.ammo[WP_MACHINEGUN]; bs->inventory[INVENTORY_GRENADES] = bs->cur_ps.ammo[WP_GRENADE_LAUNCHER]; bs->inventory[INVENTORY_CELLS] = bs->cur_ps.ammo[WP_PLASMAGUN]; bs->inventory[INVENTORY_LIGHTNINGAMMO] = bs->cur_ps.ammo[WP_LIGHTNING]; bs->inventory[INVENTORY_ROCKETS] = bs->cur_ps.ammo[WP_ROCKET_LAUNCHER]; bs->inventory[INVENTORY_SLUGS] = bs->cur_ps.ammo[WP_RAILGUN]; bs->inventory[INVENTORY_BFGAMMO] = bs->cur_ps.ammo[WP_BFG]; bs->inventory[INVENTORY_NAILS] = bs->cur_ps.ammo[WP_NAILGUN]; bs->inventory[INVENTORY_MINES] = bs->cur_ps.ammo[WP_PROX_LAUNCHER]; bs->inventory[INVENTORY_BELT] = bs->cur_ps.ammo[WP_CHAINGUN]; //powerups bs->inventory[INVENTORY_HEALTH] = bs->cur_ps.stats[STAT_HEALTH]; bs->inventory[INVENTORY_TELEPORTER] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_TELEPORTER; bs->inventory[INVENTORY_MEDKIT] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_MEDKIT; bs->inventory[INVENTORY_KAMIKAZE] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_KAMIKAZE; bs->inventory[INVENTORY_PORTAL] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_PORTAL; bs->inventory[INVENTORY_INVULNERABILITY] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_INVULNERABILITY; bs->inventory[INVENTORY_QUAD] = bs->cur_ps.powerups[PW_QUAD] != 0; bs->inventory[INVENTORY_ENVIRONMENTSUIT] = bs->cur_ps.powerups[PW_BATTLESUIT] != 0; bs->inventory[INVENTORY_HASTE] = bs->cur_ps.powerups[PW_HASTE] != 0; bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0; bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0; bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0; bs->inventory[INVENTORY_SCOUT] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_SCOUT; bs->inventory[INVENTORY_GUARD] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_GUARD; bs->inventory[INVENTORY_DOUBLER] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_DOUBLER; bs->inventory[INVENTORY_AMMOREGEN] = bs->cur_ps.stats[STAT_PERSISTANT_POWERUP] == MODELINDEX_AMMOREGEN; bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0; bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0; bs->inventory[INVENTORY_NEUTRALFLAG] = bs->cur_ps.powerups[PW_NEUTRALFLAG] != 0; if (BotTeam(bs) == TEAM_RED) { bs->inventory[INVENTORY_REDCUBE] = bs->cur_ps.generic1; bs->inventory[INVENTORY_BLUECUBE] = 0; } else { bs->inventory[INVENTORY_REDCUBE] = 0; bs->inventory[INVENTORY_BLUECUBE] = bs->cur_ps.generic1; } BotCheckItemPickup(bs, oldinventory); } /* ================== BotUpdateBattleInventory ================== */ void BotUpdateBattleInventory(bot_state_t *bs, int enemy) { vec3_t dir; aas_entityinfo_t entinfo; BotEntityInfo(enemy, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); bs->inventory[ENEMY_HEIGHT] = (int) dir[2]; dir[2] = 0; bs->inventory[ENEMY_HORIZONTAL_DIST] = (int) VectorLength(dir); //FIXME: add num visible enemies and num visible team mates to the inventory } /* ================== BotUseKamikaze ================== */ #define KAMIKAZE_DIST 1024 void BotUseKamikaze(bot_state_t *bs) { int c, teammates, enemies; aas_entityinfo_t entinfo; vec3_t dir, target; bot_goal_t *goal; bsp_trace_t trace; //if the bot has no kamikaze if (bs->inventory[INVENTORY_KAMIKAZE] <= 0) return; if (bs->kamikaze_time > FloatTime()) return; bs->kamikaze_time = FloatTime() + 0.2; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //never use kamikaze if the team flag carrier is visible if (BotCTFCarryingFlag(bs)) return; c = BotTeamFlagCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) return; } c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_1FCTF) { //never use kamikaze if the team flag carrier is visible if (Bot1FCTFCarryingFlag(bs)) return; c = BotTeamFlagCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) return; } c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_OBELISK) { switch(BotTeam(bs)) { case TEAM_RED: goal = &blueobelisk; break; default: goal = &redobelisk; break; } //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; VectorSubtract(bs->origin, target, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST * 0.9)) { BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_HARVESTER) { // if (BotHarvesterCarryingCubes(bs)) return; //never use kamikaze if a team mate carrying cubes is visible c = BotTeamCubeCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) return; } c = BotEnemyCubeCarrierVisible(bs); if (c >= 0) { BotEntityInfo(c, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(KAMIKAZE_DIST)) { trap_EA_Use(bs->client); return; } } } // BotVisibleTeamMatesAndEnemies(bs, &teammates, &enemies, KAMIKAZE_DIST); // if (enemies > 2 && enemies > teammates+1) { trap_EA_Use(bs->client); return; } } /* ================== BotUseInvulnerability ================== */ void BotUseInvulnerability(bot_state_t *bs) { int c; vec3_t dir, target; bot_goal_t *goal; bsp_trace_t trace; //if the bot has no invulnerability if (bs->inventory[INVENTORY_INVULNERABILITY] <= 0) return; if (bs->invulnerability_time > FloatTime()) return; bs->invulnerability_time = FloatTime() + 0.2; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //never use kamikaze if the team flag carrier is visible if (BotCTFCarryingFlag(bs)) return; c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) return; //if near enemy flag and the flag is visible switch(BotTeam(bs)) { case TEAM_RED: goal = &ctf_blueflag; break; default: goal = &ctf_redflag; break; } //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; VectorSubtract(bs->origin, target, dir); if (VectorLengthSquared(dir) < Square(200)) { BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_1FCTF) { //never use kamikaze if the team flag carrier is visible if (Bot1FCTFCarryingFlag(bs)) return; c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) return; //if near enemy flag and the flag is visible switch(BotTeam(bs)) { case TEAM_RED: goal = &ctf_blueflag; break; default: goal = &ctf_redflag; break; } //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; VectorSubtract(bs->origin, target, dir); if (VectorLengthSquared(dir) < Square(200)) { BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_OBELISK) { switch(BotTeam(bs)) { case TEAM_RED: goal = &blueobelisk; break; default: goal = &redobelisk; break; } //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; VectorSubtract(bs->origin, target, dir); if (VectorLengthSquared(dir) < Square(300)) { BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { trap_EA_Use(bs->client); return; } } } else if (gametype == GT_HARVESTER) { // if (BotHarvesterCarryingCubes(bs)) return; c = BotEnemyCubeCarrierVisible(bs); if (c >= 0) return; //if near enemy base and enemy base is visible switch(BotTeam(bs)) { case TEAM_RED: goal = &blueobelisk; break; default: goal = &redobelisk; break; } //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; VectorSubtract(bs->origin, target, dir); if (VectorLengthSquared(dir) < Square(200)) { BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { trap_EA_Use(bs->client); return; } } } } /* ================== BotBattleUseItems ================== */ void BotBattleUseItems(bot_state_t *bs) { if (bs->inventory[INVENTORY_HEALTH] < 40) { if (bs->inventory[INVENTORY_TELEPORTER] > 0) { if (!BotCTFCarryingFlag(bs) && !Bot1FCTFCarryingFlag(bs) && !BotHarvesterCarryingCubes(bs) ) { trap_EA_Use(bs->client); } } } if (bs->inventory[INVENTORY_HEALTH] < 60) { if (bs->inventory[INVENTORY_MEDKIT] > 0) { trap_EA_Use(bs->client); } } BotUseKamikaze(bs); BotUseInvulnerability(bs); } /* ================== BotSetTeleportTime ================== */ void BotSetTeleportTime(bot_state_t *bs) { if ((bs->cur_ps.eFlags ^ bs->last_eFlags) & EF_TELEPORT_BIT) { bs->teleport_time = FloatTime(); } bs->last_eFlags = bs->cur_ps.eFlags; } /* ================== BotIsDead ================== */ qboolean BotIsDead(bot_state_t *bs) { return (bs->cur_ps.pm_type == PM_DEAD); } /* ================== BotIsObserver ================== */ qboolean BotIsObserver(bot_state_t *bs) { char buf[MAX_INFO_STRING]; if (bs->cur_ps.pm_type == PM_SPECTATOR) return qtrue; trap_GetConfigstring(CS_PLAYERS+bs->client, buf, sizeof(buf)); if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) return qtrue; return qfalse; } /* ================== BotIntermission ================== */ qboolean BotIntermission(bot_state_t *bs) { //NOTE: we shouldn't be looking at the game code... if (level.intermissiontime) return qtrue; return (bs->cur_ps.pm_type == PM_FREEZE || bs->cur_ps.pm_type == PM_INTERMISSION); } /* ================== BotInLavaOrSlime ================== */ qboolean BotInLavaOrSlime(bot_state_t *bs) { vec3_t feet; VectorCopy(bs->origin, feet); feet[2] -= 23; return (trap_AAS_PointContents(feet) & (CONTENTS_LAVA|CONTENTS_SLIME)); } /* ================== BotCreateWayPoint ================== */ bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum) { bot_waypoint_t *wp; vec3_t waypointmins = {-8, -8, -8}, waypointmaxs = {8, 8, 8}; wp = botai_freewaypoints; if ( !wp ) { BotAI_Print( PRT_WARNING, "BotCreateWayPoint: Out of waypoints\n" ); return NULL; } botai_freewaypoints = botai_freewaypoints->next; Q_strncpyz( wp->name, name, sizeof(wp->name) ); VectorCopy(origin, wp->goal.origin); VectorCopy(waypointmins, wp->goal.mins); VectorCopy(waypointmaxs, wp->goal.maxs); wp->goal.areanum = areanum; wp->next = NULL; wp->prev = NULL; return wp; } /* ================== BotFindWayPoint ================== */ bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name) { bot_waypoint_t *wp; for (wp = waypoints; wp; wp = wp->next) { if (!Q_stricmp(wp->name, name)) return wp; } return NULL; } /* ================== BotFreeWaypoints ================== */ void BotFreeWaypoints(bot_waypoint_t *wp) { bot_waypoint_t *nextwp; for (; wp; wp = nextwp) { nextwp = wp->next; wp->next = botai_freewaypoints; botai_freewaypoints = wp; } } /* ================== BotInitWaypoints ================== */ void BotInitWaypoints(void) { int i; botai_freewaypoints = NULL; for (i = 0; i < MAX_WAYPOINTS; i++) { botai_waypoints[i].next = botai_freewaypoints; botai_freewaypoints = &botai_waypoints[i]; } } /* ================== TeamPlayIsOn ================== */ int TeamPlayIsOn(void) { return ( gametype >= GT_TEAM && g_ffa_gt!=1); } /* ================== BotAggression ================== */ float BotAggression(bot_state_t *bs) { //if the bot has quad if (bs->inventory[INVENTORY_QUAD]) { //if the bot is not holding the gauntlet or the enemy is really nearby if (bs->weaponnum != WP_GAUNTLET || bs->inventory[ENEMY_HORIZONTAL_DIST] < 80) { return 70; } } //if the enemy is located way higher than the bot if (bs->inventory[ENEMY_HEIGHT] > 200) return 0; //if the bot is very low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return 0; //if the bot is low on health if (bs->inventory[INVENTORY_HEALTH] < 80) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return 0; } //if the bot can use the bfg if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 7) return 100; //if the bot can use the railgun if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 5) return 95; //if the bot can use the lightning gun if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return 90; //if the bot can use the rocketlauncher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 5) return 90; //if the bot can use the plasmagun if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 40) return 85; //if the bot can use the grenade launcher if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 && bs->inventory[INVENTORY_GRENADES] > 10) return 80; //if the bot can use the shotgun if (bs->inventory[INVENTORY_SHOTGUN] > 0 && bs->inventory[INVENTORY_SHELLS] > 10) return 50; //otherwise the bot is not feeling too good return 0; } /* ================== BotFeelingBad ================== */ float BotFeelingBad(bot_state_t *bs) { if (bs->weaponnum == WP_GAUNTLET) { return 100; } if (bs->inventory[INVENTORY_HEALTH] < 40) { return 100; } if (bs->weaponnum == WP_MACHINEGUN) { return 90; } if (bs->inventory[INVENTORY_HEALTH] < 60) { return 80; } return 0; } /* ================== BotWantsToRetreat ================== */ int BotWantsToRetreat(bot_state_t *bs) { aas_entityinfo_t entinfo; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //always retreat when carrying a CTF flag if (BotCTFCarryingFlag(bs)) return qtrue; } else if (gametype == GT_1FCTF) { //if carrying the flag then always retreat if (Bot1FCTFCarryingFlag(bs)) return qtrue; } else if (gametype == GT_OBELISK) { //the bots should be dedicated to attacking the enemy obelisk if (bs->ltgtype == LTG_ATTACKENEMYBASE) { if (bs->enemy != redobelisk.entitynum && bs->enemy != blueobelisk.entitynum) { return qtrue; } } if (BotFeelingBad(bs) > 50) { return qtrue; } return qfalse; } else if (gametype == GT_HARVESTER) { //if carrying cubes then always retreat if (BotHarvesterCarryingCubes(bs)) return qtrue; } // if (bs->enemy >= 0) { //if the enemy is carrying a flag BotEntityInfo(bs->enemy, &entinfo); // if the enemy is carrying a flag if (EntityCarriesFlag(&entinfo)) return qfalse; #ifdef MISSIONPACK // if the enemy is carrying cubes if (EntityCarriesCubes(&entinfo)) return qfalse; #endif } //if the bot is getting the flag if (bs->ltgtype == LTG_GETFLAG) return qtrue; // if (BotAggression(bs) < 50) return qtrue; return qfalse; } /* ================== BotWantsToChase ================== */ int BotWantsToChase(bot_state_t *bs) { aas_entityinfo_t entinfo; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //never chase when carrying a CTF flag if (BotCTFCarryingFlag(bs)) return qfalse; //always chase if the enemy is carrying a flag BotEntityInfo(bs->enemy, &entinfo); if (EntityCarriesFlag(&entinfo)) return qtrue; } else if (gametype == GT_1FCTF) { //never chase if carrying the flag if (Bot1FCTFCarryingFlag(bs)) return qfalse; //always chase if the enemy is carrying a flag BotEntityInfo(bs->enemy, &entinfo); if (EntityCarriesFlag(&entinfo)) return qtrue; } else if (gametype == GT_OBELISK) { //the bots should be dedicated to attacking the enemy obelisk if (bs->ltgtype == LTG_ATTACKENEMYBASE) { if (bs->enemy != redobelisk.entitynum && bs->enemy != blueobelisk.entitynum) { return qfalse; } } } else if (gametype == GT_HARVESTER) { //never chase if carrying cubes if (BotHarvesterCarryingCubes(bs)) return qfalse; BotEntityInfo(bs->enemy, &entinfo); // always chase if the enemy is carrying cubes if (EntityCarriesCubes(&entinfo)) return qtrue; } //if the bot is getting the flag if (bs->ltgtype == LTG_GETFLAG) return qfalse; // if (BotAggression(bs) > 50) return qtrue; return qfalse; } /* ================== BotWantsToHelp ================== */ int BotWantsToHelp(bot_state_t *bs) { return qtrue; } /* ================== BotCanAndWantsToRocketJump ================== */ int BotCanAndWantsToRocketJump(bot_state_t *bs) { float rocketjumper; //if rocket jumping is disabled if (!bot_rocketjump.integer) return qfalse; //if no rocket launcher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0) return qfalse; //if low on rockets if (bs->inventory[INVENTORY_ROCKETS] < 3) return qfalse; //Sago: Special rule - always happy to rocket jump in elimination, eCTF end LMS if if ( (g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION || g_gametype.integer==GT_LMS ) && g_elimination_selfdamage.integer==0) { return qtrue; } //never rocket jump with the Quad if (bs->inventory[INVENTORY_QUAD]) return qfalse; //if low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; //if not full health if (bs->inventory[INVENTORY_HEALTH] < 90) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; } rocketjumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WEAPONJUMPING, 0, 1); if (rocketjumper < 0.5) return qfalse; return qtrue; } /* ================== BotHasPersistantPowerupAndWeapon ================== */ int BotHasPersistantPowerupAndWeapon(bot_state_t *bs) { // if the bot does not have a persistant powerup //Sago - FIXME - This causes problems if there are no persistant powerups /* if (!bs->inventory[INVENTORY_SCOUT] && !bs->inventory[INVENTORY_GUARD] && !bs->inventory[INVENTORY_DOUBLER] && !bs->inventory[INVENTORY_AMMOREGEN] ) { return qfalse; }*/ //if the bot is very low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; //if the bot is low on health if (bs->inventory[INVENTORY_HEALTH] < 80) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; } //if the bot can use the bfg if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 7) return qtrue; //if the bot can use the railgun if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 5) return qtrue; //if the bot can use the lightning gun if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return qtrue; //if the bot can use the rocketlauncher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 5) return qtrue; // if (bs->inventory[INVENTORY_NAILGUN] > 0 && bs->inventory[INVENTORY_NAILS] > 5) return qtrue; // if (bs->inventory[INVENTORY_PROXLAUNCHER] > 0 && bs->inventory[INVENTORY_MINES] > 5) return qtrue; // if (bs->inventory[INVENTORY_CHAINGUN] > 0 && bs->inventory[INVENTORY_BELT] > 40) return qtrue; //if the bot can use the plasmagun if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 20) return qtrue; return qfalse; } /* ================== BotGoCamp ================== */ void BotGoCamp(bot_state_t *bs, bot_goal_t *goal) { float camper; bs->decisionmaker = bs->client; //set message time to zero so bot will NOT show any message bs->teammessage_time = 0; //set the ltg type bs->ltgtype = LTG_CAMP; //set the team goal memcpy(&bs->teamgoal, goal, sizeof(bot_goal_t)); //get the team goal time camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); if (camper > 0.99) bs->teamgoal_time = FloatTime() + 99999; else bs->teamgoal_time = FloatTime() + 120 + 180 * camper + random() * 15; //set the last time the bot started camping bs->camp_time = FloatTime(); //the teammate that requested the camping bs->teammate = 0; //do NOT type arrive message bs->arrive_time = 1; } /* ================== BotWantsToCamp ================== */ int BotWantsToCamp(bot_state_t *bs) { float camper; int cs, traveltime, besttraveltime; bot_goal_t goal, bestgoal; camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); if (camper < 0.1) return qfalse; //if the bot has a team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL) { return qfalse; } //if camped recently if (bs->camp_time > FloatTime() - 60 + 300 * (1-camper)) return qfalse; // if (random() > camper) { bs->camp_time = FloatTime(); return qfalse; } //if the bot isn't healthy anough if (BotAggression(bs) < 50) return qfalse; //the bot should have at least have the rocket launcher, the railgun or the bfg10k with some ammo if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS] < 10) && (bs->inventory[INVENTORY_RAILGUN] <= 0 || bs->inventory[INVENTORY_SLUGS] < 10) && (bs->inventory[INVENTORY_BFG10K] <= 0 || bs->inventory[INVENTORY_BFGAMMO] < 10)) { return qfalse; } //find the closest camp spot besttraveltime = 99999; for (cs = trap_BotGetNextCampSpotGoal(0, &goal); cs; cs = trap_BotGetNextCampSpotGoal(cs, &goal)) { traveltime = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal.areanum, TFL_DEFAULT); if (traveltime && traveltime < besttraveltime) { besttraveltime = traveltime; memcpy(&bestgoal, &goal, sizeof(bot_goal_t)); } } if (besttraveltime > 150) return qfalse; //ok found a camp spot, go camp there BotGoCamp(bs, &bestgoal); bs->ordered = qfalse; // return qtrue; } /* ================== BotDontAvoid ================== */ void BotDontAvoid(bot_state_t *bs, char *itemname) { bot_goal_t goal; int num; num = trap_BotGetLevelItemGoal(-1, itemname, &goal); while(num >= 0) { trap_BotRemoveFromAvoidGoals(bs->gs, goal.number); num = trap_BotGetLevelItemGoal(num, itemname, &goal); } } /* ================== BotGoForPowerups ================== */ void BotGoForPowerups(bot_state_t *bs) { //don't avoid any of the powerups anymore BotDontAvoid(bs, "Quad Damage"); BotDontAvoid(bs, "Regeneration"); BotDontAvoid(bs, "Battle Suit"); BotDontAvoid(bs, "Speed"); BotDontAvoid(bs, "Invisibility"); //BotDontAvoid(bs, "Flight"); //reset the long term goal time so the bot will go for the powerup //NOTE: the long term goal type doesn't change bs->ltg_time = 0; } /* ================== BotRoamGoal ================== */ void BotRoamGoal(bot_state_t *bs, vec3_t goal) { int pc, i; float len, rnd; vec3_t dir, bestorg, belowbestorg; bsp_trace_t trace; for (i = 0; i < 10; i++) { //start at the bot origin VectorCopy(bs->origin, bestorg); rnd = random(); if (rnd > 0.25) { //add a random value to the x-coordinate if (random() < 0.5) bestorg[0] -= 800 * random() + 100; else bestorg[0] += 800 * random() + 100; } if (rnd < 0.75) { //add a random value to the y-coordinate if (random() < 0.5) bestorg[1] -= 800 * random() + 100; else bestorg[1] += 800 * random() + 100; } //add a random value to the z-coordinate (NOTE: 48 = maxjump?) bestorg[2] += 2 * 48 * crandom(); //trace a line from the origin to the roam target BotAI_Trace(&trace, bs->origin, NULL, NULL, bestorg, bs->entitynum, MASK_SOLID); //direction and length towards the roam target VectorSubtract(trace.endpos, bs->origin, dir); len = VectorNormalize(dir); //if the roam target is far away anough if (len > 200) { //the roam target is in the given direction before walls VectorScale(dir, len * trace.fraction - 40, dir); VectorAdd(bs->origin, dir, bestorg); //get the coordinates of the floor below the roam target belowbestorg[0] = bestorg[0]; belowbestorg[1] = bestorg[1]; belowbestorg[2] = bestorg[2] - 800; BotAI_Trace(&trace, bestorg, NULL, NULL, belowbestorg, bs->entitynum, MASK_SOLID); // if (!trace.startsolid) { trace.endpos[2]++; pc = trap_PointContents(trace.endpos, bs->entitynum); if (!(pc & (CONTENTS_LAVA | CONTENTS_SLIME))) { VectorCopy(bestorg, goal); return; } } } } VectorCopy(bestorg, goal); } /* ================== BotAttackMove ================== */ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { int movetype, i, attackentity; float attack_skill, jumper, croucher, dist, strafechange_time; float attack_dist, attack_range; vec3_t forward, backward, sideward, hordir, up = {0, 0, 1}; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; bot_goal_t goal; attackentity = bs->enemy; // if (bs->attackchase_time > FloatTime()) { //create the chase goal goal.entitynum = attackentity; goal.areanum = bs->lastenemyareanum; VectorCopy(bs->lastenemyorigin, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, tfl); return moveresult; } // memset(&moveresult, 0, sizeof(bot_moveresult_t)); // attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); jumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_JUMPER, 0, 1); croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); //if the bot is really stupid if (attack_skill < 0.2) return moveresult; //initialize the movement state BotSetupForMovement(bs); //get the enemy entity info BotEntityInfo(attackentity, &entinfo); //direction towards the enemy VectorSubtract(entinfo.origin, bs->origin, forward); //the distance towards the enemy dist = VectorNormalize(forward); VectorNegate(forward, backward); //walk, crouch or jump movetype = MOVE_WALK; // if (bs->attackcrouch_time < FloatTime() - 1) { if (random() < jumper) { movetype = MOVE_JUMP; } //wait at least one second before crouching again else if (bs->attackcrouch_time < FloatTime() - 1 && random() < croucher) { bs->attackcrouch_time = FloatTime() + croucher * 5; } } if (bs->attackcrouch_time > FloatTime()) movetype = MOVE_CROUCH; //if the bot should jump if (movetype == MOVE_JUMP) { //if jumped last frame if (bs->attackjump_time > FloatTime()) { movetype = MOVE_WALK; } else { bs->attackjump_time = FloatTime() + 1; } } if (bs->cur_ps.weapon == WP_GAUNTLET) { attack_dist = 0; attack_range = 0; } else { attack_dist = IDEAL_ATTACKDIST; attack_range = 40; } //if the bot is stupid if (attack_skill <= 0.4) { //just walk to or away from the enemy if (dist > attack_dist + attack_range) { if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; } if (dist < attack_dist - attack_range) { if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; } return moveresult; } //increase the strafe time bs->attackstrafe_time += bs->thinktime; //get the strafe change time strafechange_time = 0.4 + (1 - attack_skill) * 0.2; if (attack_skill > 0.7) strafechange_time += crandom() * 0.2; //if the strafe direction should be changed if (bs->attackstrafe_time > strafechange_time) { //some magic number :) if (random() > 0.935) { //flip the strafe direction bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } } // for (i = 0; i < 2; i++) { hordir[0] = forward[0]; hordir[1] = forward[1]; hordir[2] = 0; VectorNormalize(hordir); //get the sideward vector CrossProduct(hordir, up, sideward); //reverse the vector depending on the strafe direction if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); //randomly go back a little if (random() > 0.9) { VectorAdd(sideward, backward, sideward); } else { //walk forward or backward to get at the ideal attack distance if (dist > attack_dist + attack_range) { VectorAdd(sideward, forward, sideward); } else if (dist < attack_dist - attack_range) { VectorAdd(sideward, backward, sideward); } } //perform the movement if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) return moveresult; //movement failed, flip the strafe direction bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } //bot couldn't do any usefull movement // bs->attackchase_time = AAS_Time() + 6; return moveresult; } /* ================== BotSameTeam ================== */ int BotSameTeam(bot_state_t *bs, int entnum) { if (bs->client < 0 || bs->client >= MAX_CLIENTS) { //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); return qfalse; } if (entnum < 0 || entnum >= MAX_CLIENTS) { //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); return qfalse; } if ( gametype >= GT_TEAM && g_ffa_gt!=1) { /*Sago: I don't know why they decided to check the configstring instead of the real value. For some reason bots sometimes gets a wrong config string when chaning gametypes. Now we check the real value: */ if(level.clients[bs->client].sess.sessionTeam==level.clients[entnum].sess.sessionTeam) return qtrue; } return qfalse; } /* ================== InFieldOfVision ================== */ qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles) { int i; float diff, angle; for (i = 0; i < 2; i++) { angle = AngleMod(viewangles[i]); angles[i] = AngleMod(angles[i]); diff = angles[i] - angle; if (angles[i] > angle) { if (diff > 180.0) diff -= 360.0; } else { if (diff < -180.0) diff += 360.0; } if (diff > 0) { if (diff > fov * 0.5) return qfalse; } else { if (diff < -fov * 0.5) return qfalse; } } return qtrue; } /* ================== BotEntityVisible returns visibility in the range [0, 1] taking fog and water surfaces into account ================== */ float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent) { int i, contents_mask, passent, hitent, infog, inwater, otherinfog, pc; float squaredfogdist, waterfactor, vis, bestvis; bsp_trace_t trace; aas_entityinfo_t entinfo; vec3_t dir, entangles, start, end, middle; //calculate middle of bounding box BotEntityInfo(ent, &entinfo); VectorAdd(entinfo.mins, entinfo.maxs, middle); VectorScale(middle, 0.5, middle); VectorAdd(entinfo.origin, middle, middle); //check if entity is within field of vision VectorSubtract(middle, eye, dir); vectoangles(dir, entangles); if (!InFieldOfVision(viewangles, fov, entangles)) return 0; // pc = trap_AAS_PointContents(eye); infog = (pc & CONTENTS_FOG); inwater = (pc & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)); // bestvis = 0; for (i = 0; i < 3; i++) { //if the point is not in potential visible sight //if (!AAS_inPVS(eye, middle)) continue; // contents_mask = CONTENTS_SOLID|CONTENTS_PLAYERCLIP; passent = viewer; hitent = ent; VectorCopy(eye, start); VectorCopy(middle, end); //if the entity is in water, lava or slime if (trap_AAS_PointContents(middle) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { contents_mask |= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); } //if eye is in water, lava or slime if (inwater) { if (!(contents_mask & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) { passent = ent; hitent = viewer; VectorCopy(middle, start); VectorCopy(eye, end); } contents_mask ^= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); } //trace from start to end BotAI_Trace(&trace, start, NULL, NULL, end, passent, contents_mask); //if water was hit waterfactor = 1.0; if (trace.contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { //if the water surface is translucent if (1) { //trace through the water contents_mask &= ~(CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); BotAI_Trace(&trace, trace.endpos, NULL, NULL, end, passent, contents_mask); waterfactor = 0.5; } } //if a full trace or the hitent was hit if (trace.fraction >= 1 || trace.ent == hitent) { //check for fog, assuming there's only one fog brush where //either the viewer or the entity is in or both are in otherinfog = (trap_AAS_PointContents(middle) & CONTENTS_FOG); if (infog && otherinfog) { VectorSubtract(trace.endpos, eye, dir); squaredfogdist = VectorLengthSquared(dir); } else if (infog) { VectorCopy(trace.endpos, start); BotAI_Trace(&trace, start, NULL, NULL, eye, viewer, CONTENTS_FOG); VectorSubtract(eye, trace.endpos, dir); squaredfogdist = VectorLengthSquared(dir); } else if (otherinfog) { VectorCopy(trace.endpos, end); BotAI_Trace(&trace, eye, NULL, NULL, end, viewer, CONTENTS_FOG); VectorSubtract(end, trace.endpos, dir); squaredfogdist = VectorLengthSquared(dir); } else { //if the entity and the viewer are not in fog assume there's no fog in between squaredfogdist = 0; } //decrease visibility with the view distance through fog vis = 1 / ((squaredfogdist * 0.001) < 1 ? 1 : (squaredfogdist * 0.001)); //if entering water visibility is reduced vis *= waterfactor; // if (vis > bestvis) bestvis = vis; //if pretty much no fog if (bestvis >= 0.95) return bestvis; } //check bottom and top of bounding box as well if (i == 0) middle[2] += entinfo.mins[2]; else if (i == 1) middle[2] += entinfo.maxs[2] - entinfo.mins[2]; } return bestvis; } /* ================== BotFindEnemy ================== */ int BotFindEnemy(bot_state_t *bs, int curenemy) { int i, healthdecrease; float f, alertness, easyfragger, vis; float squaredist, cursquaredist; aas_entityinfo_t entinfo, curenemyinfo; vec3_t dir, angles; alertness = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ALERTNESS, 0, 1); easyfragger = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1); //check if the health decreased healthdecrease = bs->lasthealth > bs->inventory[INVENTORY_HEALTH]; //remember the current health value bs->lasthealth = bs->inventory[INVENTORY_HEALTH]; // if (curenemy >= 0) { BotEntityInfo(curenemy, &curenemyinfo); if (EntityCarriesFlag(&curenemyinfo)) return qfalse; VectorSubtract(curenemyinfo.origin, bs->origin, dir); cursquaredist = VectorLengthSquared(dir); } else { cursquaredist = 0; } if (gametype == GT_OBELISK) { vec3_t target; bot_goal_t *goal; bsp_trace_t trace; if (BotTeam(bs) == TEAM_RED) goal = &blueobelisk; else goal = &redobelisk; //if the obelisk is visible VectorCopy(goal->origin, target); target[2] += 1; BotAI_Trace(&trace, bs->eye, NULL, NULL, target, bs->client, CONTENTS_SOLID); if (trace.fraction >= 1 || trace.ent == goal->entitynum) { if (goal->entitynum == bs->enemy) { return qfalse; } bs->enemy = goal->entitynum; bs->enemysight_time = FloatTime(); bs->enemysuicide = qfalse; bs->enemydeath_time = 0; bs->enemyvisible_time = FloatTime(); return qtrue; } } // for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; //if it's the current enemy if (i == curenemy) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; //if the enemy is invisible and not shooting if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } //Neil Torontos unlagged //unlagged - misc // this has nothing to do with lag compensation, but it's great for testing if ( g_entities[i].flags & FL_NOTARGET ) continue; //unlagged - misc //if not an easy fragger don't shoot at chatting players if (easyfragger < 0.5 && EntityIsChatting(&entinfo)) continue; // if (lastteleport_time > FloatTime() - 3) { VectorSubtract(entinfo.origin, lastteleport_origin, dir); if (VectorLengthSquared(dir) < Square(70)) continue; } //calculate the distance towards the enemy VectorSubtract(entinfo.origin, bs->origin, dir); squaredist = VectorLengthSquared(dir); //if this entity is not carrying a flag if (!EntityCarriesFlag(&entinfo)) { //if this enemy is further away than the current one if (curenemy >= 0 && squaredist > cursquaredist) continue; } //end if //if the bot has no if (squaredist > Square(900.0 + alertness * 4000.0)) continue; //if on the same team if (BotSameTeam(bs, i)) continue; //if the bot's health decreased or the enemy is shooting if (curenemy < 0 && (healthdecrease || EntityIsShooting(&entinfo))) f = 360; else f = 90 + 90 - (90 - (squaredist > Square(810) ? Square(810) : squaredist) / (810 * 9)); //check if the enemy is visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, f, i); if (vis <= 0) continue; //if the enemy is quite far away, not shooting and the bot is not damaged if (curenemy < 0 && squaredist > Square(100) && !healthdecrease && !EntityIsShooting(&entinfo)) { //check if we can avoid this enemy VectorSubtract(bs->origin, entinfo.origin, dir); vectoangles(dir, angles); //if the bot isn't in the fov of the enemy if (!InFieldOfVision(entinfo.angles, 90, angles)) { //update some stuff for this enemy BotUpdateBattleInventory(bs, i); //if the bot doesn't really want to fight if (BotWantsToRetreat(bs)) continue; } } //found an enemy bs->enemy = entinfo.number; if (curenemy >= 0) bs->enemysight_time = FloatTime() - 2; else bs->enemysight_time = FloatTime(); bs->enemysuicide = qfalse; bs->enemydeath_time = 0; bs->enemyvisible_time = FloatTime(); return qtrue; } return qfalse; } /* ================== BotTeamFlagCarrierVisible ================== */ int BotTeamFlagCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is not on the same team if (!BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } /* ================== BotTeamFlagCarrier ================== */ int BotTeamFlagCarrier(bot_state_t *bs) { int i; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is not on the same team if (!BotSameTeam(bs, i)) continue; // return i; } return -1; } /* ================== BotEnemyFlagCarrierVisible ================== */ int BotEnemyFlagCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is on the same team if (BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } /* ================== BotVisibleTeamMatesAndEnemies ================== */ void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range) { int i; float vis; aas_entityinfo_t entinfo; vec3_t dir; if (teammates) *teammates = 0; if (enemies) *enemies = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if not within range VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) > Square(range)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; //if the flag carrier is on the same team if (BotSameTeam(bs, i)) { if (teammates) (*teammates)++; } else { if (enemies) (*enemies)++; } } } /* ================== BotTeamCubeCarrierVisible ================== */ int BotTeamCubeCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesCubes(&entinfo)) continue; //if the flag carrier is not on the same team if (!BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } /* ================== BotEnemyCubeCarrierVisible ================== */ int BotEnemyCubeCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesCubes(&entinfo)) continue; //if the flag carrier is on the same team if (BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } //#endif /* ================== BotAimAtEnemy ================== */ void BotAimAtEnemy(bot_state_t *bs) { int i, enemyvisible; float dist, f, aim_skill, aim_accuracy, speed, reactiontime; vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity; vec3_t mins = {-4,-4,-4}, maxs = {4, 4, 4}; weaponinfo_t wi; aas_entityinfo_t entinfo; bot_goal_t goal; bsp_trace_t trace; vec3_t target; //if the bot has no enemy if (bs->enemy < 0) { return; } //get the enemy entity information BotEntityInfo(bs->enemy, &entinfo); //if this is not a player (should be an obelisk) if (bs->enemy >= MAX_CLIENTS) { //if the obelisk is visible VectorCopy(entinfo.origin, target); // if attacking an obelisk if ( bs->enemy == redobelisk.entitynum || bs->enemy == blueobelisk.entitynum ) { target[2] += 32; } //aim at the obelisk VectorSubtract(target, bs->eye, dir); vectoangles(dir, bs->ideal_viewangles); //set the aim target before trying to attack VectorCopy(target, bs->aimtarget); return; } // //BotAI_Print(PRT_MESSAGE, "client %d: aiming at client %d\n", bs->entitynum, bs->enemy); // aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1); aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); // if (aim_skill > 0.95) { //don't aim too early reactiontime = 0.5 * trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); if (bs->enemysight_time > FloatTime() - reactiontime) return; if (bs->teleport_time > FloatTime() - reactiontime) return; } //get the weapon information trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); //get the weapon specific aim accuracy and or aim skill if (wi.number == WP_MACHINEGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN, 0, 1); } else if (wi.number == WP_SHOTGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_SHOTGUN, 0, 1); } else if (wi.number == WP_GRENADE_LAUNCHER) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1); } else if (wi.number == WP_ROCKET_LAUNCHER) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1); } else if (wi.number == WP_LIGHTNING) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_LIGHTNING, 0, 1); } else if (wi.number == WP_RAILGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_RAILGUN, 0, 1); } else if (wi.number == WP_PLASMAGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_PLASMAGUN, 0, 1); } else if (wi.number == WP_BFG) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_BFG10K, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_BFG10K, 0, 1); } // if (aim_accuracy <= 0) aim_accuracy = 0.0001f; //get the enemy entity information BotEntityInfo(bs->enemy, &entinfo); //if the enemy is invisible then shoot crappy most of the time if (EntityIsInvisible(&entinfo)) { if (random() > 0.1) aim_accuracy *= 0.4f; } // VectorSubtract(entinfo.origin, entinfo.lastvisorigin, enemyvelocity); VectorScale(enemyvelocity, 1 / entinfo.update_time, enemyvelocity); //enemy origin and velocity is remembered every 0.5 seconds if (bs->enemyposition_time < FloatTime()) { // bs->enemyposition_time = FloatTime() + 0.5; VectorCopy(enemyvelocity, bs->enemyvelocity); VectorCopy(entinfo.origin, bs->enemyorigin); } //if not extremely skilled if (aim_skill < 0.9) { VectorSubtract(entinfo.origin, bs->enemyorigin, dir); //if the enemy moved a bit if (VectorLengthSquared(dir) > Square(48)) { //if the enemy changed direction if (DotProduct(bs->enemyvelocity, enemyvelocity) < 0) { //aim accuracy should be worse now aim_accuracy *= 0.7f; } } } //check visibility of enemy enemyvisible = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy); //if the enemy is visible if (enemyvisible) { // VectorCopy(entinfo.origin, bestorigin); bestorigin[2] += 8; //get the start point shooting from //NOTE: the x and y projectile start offsets are ignored VectorCopy(bs->origin, start); start[2] += bs->cur_ps.viewheight; start[2] += wi.offset[2]; // BotAI_Trace(&trace, start, mins, maxs, bestorigin, bs->entitynum, MASK_SHOT); //if the enemy is NOT hit if (trace.fraction <= 1 && trace.ent != entinfo.number) { bestorigin[2] += 16; } //if it is not an instant hit weapon the bot might want to predict the enemy if (wi.speed) { // VectorSubtract(bestorigin, bs->origin, dir); dist = VectorLength(dir); VectorSubtract(entinfo.origin, bs->enemyorigin, dir); //if the enemy is NOT pretty far away and strafing just small steps left and right if (!(dist > 100 && VectorLengthSquared(dir) < Square(32))) { //if skilled anough do exact prediction if (aim_skill > 0.8 && //if the weapon is ready to fire bs->cur_ps.weaponstate == WEAPON_READY) { aas_clientmove_t move; vec3_t origin; VectorSubtract(entinfo.origin, bs->origin, dir); //distance towards the enemy dist = VectorLength(dir); //direction the enemy is moving in VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); // VectorScale(dir, 1 / entinfo.update_time, dir); // VectorCopy(entinfo.origin, origin); origin[2] += 1; // VectorClear(cmdmove); //AAS_ClearShownDebugLines(); trap_AAS_PredictClientMovement(&move, bs->enemy, origin, PRESENCE_CROUCH, qfalse, dir, cmdmove, 0, dist * 10 / wi.speed, 0.1f, 0, 0, qfalse); VectorCopy(move.endpos, bestorigin); //BotAI_Print(PRT_MESSAGE, "%1.1f predicted speed = %f, frames = %f\n", FloatTime(), VectorLength(dir), dist * 10 / wi.speed); } //if not that skilled do linear prediction else if (aim_skill > 0.4) { VectorSubtract(entinfo.origin, bs->origin, dir); //distance towards the enemy dist = VectorLength(dir); //direction the enemy is moving in VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); dir[2] = 0; // speed = VectorNormalize(dir) / entinfo.update_time; //botimport.Print(PRT_MESSAGE, "speed = %f, wi->speed = %f\n", speed, wi->speed); //best spot to aim at VectorMA(entinfo.origin, (dist / wi.speed) * speed, dir, bestorigin); } } } //if the projectile does radial damage if (aim_skill > 0.6 && wi.proj.damagetype & DAMAGETYPE_RADIAL) { //if the enemy isn't standing significantly higher than the bot if (entinfo.origin[2] < bs->origin[2] + 16) { //try to aim at the ground in front of the enemy VectorCopy(entinfo.origin, end); end[2] -= 64; BotAI_Trace(&trace, entinfo.origin, NULL, NULL, end, entinfo.number, MASK_SHOT); // VectorCopy(bestorigin, groundtarget); if (trace.startsolid) groundtarget[2] = entinfo.origin[2] - 16; else groundtarget[2] = trace.endpos[2] - 8; //trace a line from projectile start to ground target BotAI_Trace(&trace, start, NULL, NULL, groundtarget, bs->entitynum, MASK_SHOT); //if hitpoint is not vertically too far from the ground target if (fabs(trace.endpos[2] - groundtarget[2]) < 50) { VectorSubtract(trace.endpos, groundtarget, dir); //if the hitpoint is near anough the ground target if (VectorLengthSquared(dir) < Square(60)) { VectorSubtract(trace.endpos, start, dir); //if the hitpoint is far anough from the bot if (VectorLengthSquared(dir) > Square(100)) { //check if the bot is visible from the ground target trace.endpos[2] += 1; BotAI_Trace(&trace, trace.endpos, NULL, NULL, entinfo.origin, entinfo.number, MASK_SHOT); if (trace.fraction >= 1) { //botimport.Print(PRT_MESSAGE, "%1.1f aiming at ground\n", AAS_Time()); VectorCopy(groundtarget, bestorigin); } } } } } } bestorigin[0] += 20 * crandom() * (1 - aim_accuracy); bestorigin[1] += 20 * crandom() * (1 - aim_accuracy); bestorigin[2] += 10 * crandom() * (1 - aim_accuracy); } else { // VectorCopy(bs->lastenemyorigin, bestorigin); bestorigin[2] += 8; //if the bot is skilled anough if (aim_skill > 0.5) { //do prediction shots around corners if (wi.number == WP_BFG || wi.number == WP_ROCKET_LAUNCHER || wi.number == WP_GRENADE_LAUNCHER) { //create the chase goal goal.entitynum = bs->client; goal.areanum = bs->areanum; VectorCopy(bs->eye, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); // if (trap_BotPredictVisiblePosition(bs->lastenemyorigin, bs->lastenemyareanum, &goal, TFL_DEFAULT, target)) { VectorSubtract(target, bs->eye, dir); if (VectorLengthSquared(dir) > Square(80)) { VectorCopy(target, bestorigin); bestorigin[2] -= 20; } } aim_accuracy = 1; } } } // if (enemyvisible) { BotAI_Trace(&trace, bs->eye, NULL, NULL, bestorigin, bs->entitynum, MASK_SHOT); VectorCopy(trace.endpos, bs->aimtarget); } else { VectorCopy(bestorigin, bs->aimtarget); } //get aim direction VectorSubtract(bestorigin, bs->eye, dir); // if (wi.number == WP_MACHINEGUN || wi.number == WP_SHOTGUN || wi.number == WP_LIGHTNING || wi.number == WP_RAILGUN) { //distance towards the enemy dist = VectorLength(dir); if (dist > 150) dist = 150; f = 0.6 + dist / 150 * 0.4; aim_accuracy *= f; } //add some random stuff to the aim direction depending on the aim accuracy if (aim_accuracy < 0.8) { VectorNormalize(dir); for (i = 0; i < 3; i++) dir[i] += 0.3 * crandom() * (1 - aim_accuracy); } //set the ideal view angles vectoangles(dir, bs->ideal_viewangles); //take the weapon spread into account for lower skilled bots bs->ideal_viewangles[PITCH] += 6 * wi.vspread * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); bs->ideal_viewangles[YAW] += 6 * wi.hspread * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); //if the bots should be really challenging if (bot_challenge.integer) { //if the bot is really accurate and has the enemy in view for some time if (aim_accuracy > 0.9 && bs->enemysight_time < FloatTime() - 1) { //set the view angles directly if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; VectorCopy(bs->ideal_viewangles, bs->viewangles); trap_EA_View(bs->client, bs->viewangles); } } } /* ================== BotCheckAttack ================== */ void BotCheckAttack(bot_state_t *bs) { float points, reactiontime, fov, firethrottle; int attackentity; bsp_trace_t bsptrace; //float selfpreservation; vec3_t forward, right, start, end, dir, angles; weaponinfo_t wi; bsp_trace_t trace; aas_entityinfo_t entinfo; vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; attackentity = bs->enemy; // BotEntityInfo(attackentity, &entinfo); // if not attacking a player if (attackentity >= MAX_CLIENTS) { // if attacking an obelisk if ( entinfo.number == redobelisk.entitynum || entinfo.number == blueobelisk.entitynum ) { // if obelisk is respawning return if ( g_entities[entinfo.number].activator && g_entities[entinfo.number].activator->s.frame == 2 ) { return; } } } // reactiontime = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); if (bs->enemysight_time > FloatTime() - reactiontime) return; if (bs->teleport_time > FloatTime() - reactiontime) return; //if changing weapons if (bs->weaponchange_time > FloatTime() - 0.1) return; //check fire throttle characteristic if (bs->firethrottlewait_time > FloatTime()) return; firethrottle = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_FIRETHROTTLE, 0, 1); if (bs->firethrottleshoot_time < FloatTime()) { if (random() > firethrottle) { bs->firethrottlewait_time = FloatTime() + firethrottle; bs->firethrottleshoot_time = 0; } else { bs->firethrottleshoot_time = FloatTime() + 1 - firethrottle; bs->firethrottlewait_time = 0; } } // // VectorSubtract(bs->aimtarget, bs->eye, dir); // if (bs->weaponnum == WP_GAUNTLET) { if (VectorLengthSquared(dir) > Square(60)) { return; } } if (VectorLengthSquared(dir) < Square(100)) fov = 120; else fov = 50; // vectoangles(dir, angles); if (!InFieldOfVision(bs->viewangles, fov, angles)) return; BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->aimtarget, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (bsptrace.fraction < 1 && bsptrace.ent != attackentity) return; //get the weapon info trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); //get the start point shooting from VectorCopy(bs->origin, start); start[2] += bs->cur_ps.viewheight; AngleVectors(bs->viewangles, forward, right, NULL); start[0] += forward[0] * wi.offset[0] + right[0] * wi.offset[1]; start[1] += forward[1] * wi.offset[0] + right[1] * wi.offset[1]; start[2] += forward[2] * wi.offset[0] + right[2] * wi.offset[1] + wi.offset[2]; //end point aiming at VectorMA(start, 1000, forward, end); //a little back to make sure not inside a very close enemy VectorMA(start, -12, forward, start); BotAI_Trace(&trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT); //if the entity is a client if (trace.ent >= 0 && trace.ent < MAX_CLIENTS) { if (trace.ent != attackentity) { //if a teammate is hit if (BotSameTeam(bs, trace.ent)) return; } } //if won't hit the enemy or not attacking a player (obelisk) if (trace.ent != attackentity || attackentity >= MAX_CLIENTS) { //if the projectile does radial damage if (wi.proj.damagetype & DAMAGETYPE_RADIAL) { if (trace.fraction * 1000 < wi.proj.radius) { points = (wi.proj.damage - 0.5 * trace.fraction * 1000) * 0.5; if (points > 0) { return; } } //FIXME: check if a teammate gets radial damage } } //if fire has to be release to activate weapon if (wi.flags & WFL_FIRERELEASED) { if (bs->flags & BFL_ATTACKED) { trap_EA_Attack(bs->client); } } else { trap_EA_Attack(bs->client); } bs->flags ^= BFL_ATTACKED; } /* ================== BotMapScripts ================== */ void BotMapScripts(bot_state_t *bs) { char info[1024]; char mapname[128]; int i, shootbutton; float aim_accuracy; aas_entityinfo_t entinfo; vec3_t dir; trap_GetServerinfo(info, sizeof(info)); strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; if (!Q_stricmp(mapname, "q3tourney6")) { vec3_t mins = {700, 204, 672}, maxs = {964, 468, 680}; vec3_t buttonorg = {304, 352, 920}; //NOTE: NEVER use the func_bobbing in q3tourney6 bs->tfl &= ~TFL_FUNCBOB; //if the bot is below the bounding box if (bs->origin[0] > mins[0] && bs->origin[0] < maxs[0]) { if (bs->origin[1] > mins[1] && bs->origin[1] < maxs[1]) { if (bs->origin[2] < mins[2]) { return; } } } shootbutton = qfalse; //if an enemy is below this bounding box then shoot the button for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; // if (entinfo.origin[0] > mins[0] && entinfo.origin[0] < maxs[0]) { if (entinfo.origin[1] > mins[1] && entinfo.origin[1] < maxs[1]) { if (entinfo.origin[2] < mins[2]) { //if there's a team mate below the crusher if (BotSameTeam(bs, i)) { shootbutton = qfalse; break; } else { shootbutton = qtrue; } } } } } if (shootbutton) { bs->flags |= BFL_IDEALVIEWSET; VectorSubtract(buttonorg, bs->eye, dir); vectoangles(dir, bs->ideal_viewangles); aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); bs->ideal_viewangles[PITCH] += 8 * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); bs->ideal_viewangles[YAW] += 8 * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); // if (InFieldOfVision(bs->viewangles, 20, bs->ideal_viewangles)) { trap_EA_Attack(bs->client); } } } else if (!Q_stricmp(mapname, "mpq3tourney6")) { //NOTE: NEVER use the func_bobbing in mpq3tourney6 bs->tfl &= ~TFL_FUNCBOB; } } /* ================== BotSetMovedir ================== */ // bk001205 - made these static static vec3_t VEC_UP = {0, -1, 0}; static vec3_t MOVEDIR_UP = {0, 0, 1}; static vec3_t VEC_DOWN = {0, -2, 0}; static vec3_t MOVEDIR_DOWN = {0, 0, -1}; void BotSetMovedir(vec3_t angles, vec3_t movedir) { if (VectorCompare(angles, VEC_UP)) { VectorCopy(MOVEDIR_UP, movedir); } else if (VectorCompare(angles, VEC_DOWN)) { VectorCopy(MOVEDIR_DOWN, movedir); } else { AngleVectors(angles, movedir, NULL, NULL); } } /* ================== BotModelMinsMaxs this is ugly ================== */ int BotModelMinsMaxs(int modelindex, int eType, int contents, vec3_t mins, vec3_t maxs) { gentity_t *ent; int i; ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( eType && ent->s.eType != eType) { continue; } if ( contents && ent->r.contents != contents) { continue; } if (ent->s.modelindex == modelindex) { if (mins) VectorAdd(ent->r.currentOrigin, ent->r.mins, mins); if (maxs) VectorAdd(ent->r.currentOrigin, ent->r.maxs, maxs); return i; } } if (mins) VectorClear(mins); if (maxs) VectorClear(maxs); return 0; } /* ================== BotFuncButtonGoal ================== */ int BotFuncButtonActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int i, areas[10], numareas, modelindex, entitynum; char model[128]; float lip, dist, health, angle; vec3_t size, start, end, mins, maxs, angles, points[10]; vec3_t movedir, origin, goalorigin, bboxmins, bboxmaxs; vec3_t extramins = {1, 1, 1}, extramaxs = {-1, -1, -1}; bsp_trace_t bsptrace; activategoal->shoot = qfalse; VectorClear(activategoal->target); //create a bot goal towards the button trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //get the lip of the button trap_AAS_FloatForBSPEpairKey(bspent, "lip", &lip); if (!lip) lip = 4; //get the move direction from the angle trap_AAS_FloatForBSPEpairKey(bspent, "angle", &angle); VectorSet(angles, 0, angle, 0); BotSetMovedir(angles, movedir); //button size VectorSubtract(maxs, mins, size); //button origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); //touch distance of the button dist = fabs(movedir[0]) * size[0] + fabs(movedir[1]) * size[1] + fabs(movedir[2]) * size[2]; dist *= 0.5; // trap_AAS_FloatForBSPEpairKey(bspent, "health", &health); //if the button is shootable if (health) { //calculate the shoot target VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, activategoal->target); activategoal->shoot = qtrue; // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, goalorigin, bs->entitynum, MASK_SHOT); // if the button is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == entitynum) { // activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable button activategoal->goal.number = 0; activategoal->goal.flags = 0; VectorCopy(bs->origin, activategoal->goal.origin); activategoal->goal.areanum = bs->areanum; VectorSet(activategoal->goal.mins, -8, -8, -8); VectorSet(activategoal->goal.maxs, 8, 8, 8); // return qtrue; } else { //create a goal from where the button is visible and shoot at the button from there //add bounding box size to the dist trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); for (i = 0; i < 3; i++) { if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); else dist += fabs(movedir[i]) * fabs(bboxmins[i]); } //calculate the goal origin VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 512; numareas = trap_AAS_TraceAreas(start, end, areas, points, 10); // for (i = numareas-1; i >= 0; i--) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < 0) { // FIXME: trace forward and maybe in other directions to find a valid area } if (i >= 0) { // VectorCopy(points[i], activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSet(activategoal->goal.mins, 8, 8, 8); VectorSet(activategoal->goal.maxs, -8, -8, -8); // for (i = 0; i < 3; i++) { if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); } //end for // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } } return qfalse; } else { //add bounding box size to the dist trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); for (i = 0; i < 3; i++) { if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); else dist += fabs(movedir[i]) * fabs(bboxmins[i]); } //calculate the goal origin VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 100; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); // for (i = 0; i < numareas; i++) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < numareas) { // VectorCopy(origin, activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSubtract(mins, origin, activategoal->goal.mins); VectorSubtract(maxs, origin, activategoal->goal.maxs); // for (i = 0; i < 3; i++) { if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); } //end for // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } } return qfalse; } /* ================== BotFuncDoorGoal ================== */ int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int modelindex, entitynum; char model[MAX_INFO_STRING]; vec3_t mins, maxs, origin, angles; //shoot at the shootable door trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //door origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); VectorCopy(origin, activategoal->target); activategoal->shoot = qtrue; // activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable door activategoal->goal.number = 0; activategoal->goal.flags = 0; VectorCopy(bs->origin, activategoal->goal.origin); activategoal->goal.areanum = bs->areanum; VectorSet(activategoal->goal.mins, -8, -8, -8); VectorSet(activategoal->goal.maxs, 8, 8, 8); return qtrue; } /* ================== BotTriggerMultipleGoal ================== */ int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int i, areas[10], numareas, modelindex, entitynum; char model[128]; vec3_t start, end, mins, maxs, angles; vec3_t origin, goalorigin; activategoal->shoot = qfalse; VectorClear(activategoal->target); //create a bot goal towards the trigger trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs); //trigger origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); VectorCopy(origin, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 100; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); // for (i = 0; i < numareas; i++) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < numareas) { VectorCopy(origin, activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSubtract(mins, origin, activategoal->goal.mins); VectorSubtract(maxs, origin, activategoal->goal.maxs); // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } return qfalse; } /* ================== BotPopFromActivateGoalStack ================== */ int BotPopFromActivateGoalStack(bot_state_t *bs) { if (!bs->activatestack) return qfalse; BotEnableActivateGoalAreas(bs->activatestack, qtrue); bs->activatestack->inuse = qfalse; bs->activatestack->justused_time = FloatTime(); bs->activatestack = bs->activatestack->next; return qtrue; } /* ================== BotPushOntoActivateGoalStack ================== */ int BotPushOntoActivateGoalStack(bot_state_t *bs, bot_activategoal_t *activategoal) { int i, best; float besttime; best = -1; besttime = FloatTime() + 9999; // for (i = 0; i < MAX_ACTIVATESTACK; i++) { if (!bs->activategoalheap[i].inuse) { if (bs->activategoalheap[i].justused_time < besttime) { besttime = bs->activategoalheap[i].justused_time; best = i; } } } if (best != -1) { memcpy(&bs->activategoalheap[best], activategoal, sizeof(bot_activategoal_t)); bs->activategoalheap[best].inuse = qtrue; bs->activategoalheap[best].next = bs->activatestack; bs->activatestack = &bs->activategoalheap[best]; return qtrue; } return qfalse; } /* ================== BotClearActivateGoalStack ================== */ void BotClearActivateGoalStack(bot_state_t *bs) { while(bs->activatestack) BotPopFromActivateGoalStack(bs); } /* ================== BotEnableActivateGoalAreas ================== */ void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable) { int i; if (activategoal->areasdisabled == !enable) return; for (i = 0; i < activategoal->numareas; i++) trap_AAS_EnableRoutingArea( activategoal->areas[i], enable ); activategoal->areasdisabled = !enable; } /* ================== BotIsGoingToActivateEntity ================== */ int BotIsGoingToActivateEntity(bot_state_t *bs, int entitynum) { bot_activategoal_t *a; int i; for (a = bs->activatestack; a; a = a->next) { if (a->time < FloatTime()) continue; if (a->goal.entitynum == entitynum) return qtrue; } for (i = 0; i < MAX_ACTIVATESTACK; i++) { if (bs->activategoalheap[i].inuse) continue; // if (bs->activategoalheap[i].goal.entitynum == entitynum) { // if the bot went for this goal less than 2 seconds ago if (bs->activategoalheap[i].justused_time > FloatTime() - 2) return qtrue; } } return qfalse; } /* ================== BotGetActivateGoal returns the number of the bsp entity to activate goal->entitynum will be set to the game entity to activate ================== */ //#define OBSTACLEDEBUG int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activategoal) { int i, ent, cur_entities[10], spawnflags, modelindex, areas[MAX_ACTIVATEAREAS*2], numareas, t; char model[MAX_INFO_STRING], tmpmodel[128]; char target[128], classname[128]; float health; char targetname[10][128]; aas_entityinfo_t entinfo; aas_areainfo_t areainfo; vec3_t origin, angles, absmins, absmaxs; memset(activategoal, 0, sizeof(bot_activategoal_t)); BotEntityInfo(entitynum, &entinfo); Com_sprintf(model, sizeof( model ), "*%d", entinfo.modelindex); for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "model", tmpmodel, sizeof(tmpmodel))) continue; if (!strcmp(model, tmpmodel)) break; } if (!ent) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity found with model %s\n", model); return 0; } trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname)); if (!*classname) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model %s has no classname\n", model); return 0; } //if it is a door if (!strcmp(classname, "func_door")) { if (trap_AAS_FloatForBSPEpairKey(ent, "health", &health)) { //if the door has health then the door must be shot to open if (health) { BotFuncDoorActivateGoal(bs, ent, activategoal); return ent; } } // trap_AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); // if the door starts open then just wait for the door to return if ( spawnflags & 1 ) return 0; //get the door origin if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) { VectorClear(origin); } //if the door is open or opening already if (!VectorCompare(origin, entinfo.origin)) return 0; // store all the areas the door is in trap_AAS_ValueForBSPEpairKey(ent, "model", model, sizeof(model)); if (*model) { modelindex = atoi(model+1); if (modelindex) { VectorClear(angles); BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs); // numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS*2); // store the areas with reachabilities first for (i = 0; i < numareas; i++) { if (activategoal->numareas >= MAX_ACTIVATEAREAS) break; if ( !trap_AAS_AreaReachability(areas[i]) ) { continue; } trap_AAS_AreaInfo(areas[i], &areainfo); if (areainfo.contents & AREACONTENTS_MOVER) { activategoal->areas[activategoal->numareas++] = areas[i]; } } // store any remaining areas for (i = 0; i < numareas; i++) { if (activategoal->numareas >= MAX_ACTIVATEAREAS) break; if ( trap_AAS_AreaReachability(areas[i]) ) { continue; } trap_AAS_AreaInfo(areas[i], &areainfo); if (areainfo.contents & AREACONTENTS_MOVER) { activategoal->areas[activategoal->numareas++] = areas[i]; } } } } } // if the bot is blocked by or standing on top of a button if (!strcmp(classname, "func_button")) { return 0; } // get the targetname so we can find an entity with a matching target if (!trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[0], sizeof(targetname[0]))) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model \"%s\" has no targetname\n", model); } return 0; } // allow tree-like activation cur_entities[0] = trap_AAS_NextBSPEntity(0); for (i = 0; i >= 0 && i < 10;) { for (ent = cur_entities[i]; ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "target", target, sizeof(target))) continue; if (!strcmp(targetname[i], target)) { cur_entities[i] = trap_AAS_NextBSPEntity(ent); break; } } if (!ent) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity with target \"%s\"\n", targetname[i]); } i--; continue; } if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname))) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with target \"%s\" has no classname\n", targetname[i]); } continue; } // BSP button model if (!strcmp(classname, "func_button")) { // if (!BotFuncButtonActivateGoal(bs, ent, activategoal)) continue; // if the bot tries to activate this button already if ( bs->activatestack && bs->activatestack->inuse && bs->activatestack->goal.entitynum == activategoal->goal.entitynum && bs->activatestack->time > FloatTime() && bs->activatestack->start_time < FloatTime() - 2) continue; // if the bot is in a reachability area if ( trap_AAS_AreaReachability(bs->areanum) ) { // disable all areas the blocking entity is in BotEnableActivateGoalAreas( activategoal, qfalse ); // t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); // if the button is not reachable if (!t) { continue; } activategoal->time = FloatTime() + t * 0.01 + 5; } return ent; } // invisible trigger multiple box else if (!strcmp(classname, "trigger_multiple")) { // if (!BotTriggerMultipleActivateGoal(bs, ent, activategoal)) continue; // if the bot tries to activate this trigger already if ( bs->activatestack && bs->activatestack->inuse && bs->activatestack->goal.entitynum == activategoal->goal.entitynum && bs->activatestack->time > FloatTime() && bs->activatestack->start_time < FloatTime() - 2) continue; // if the bot is in a reachability area if ( trap_AAS_AreaReachability(bs->areanum) ) { // disable all areas the blocking entity is in BotEnableActivateGoalAreas( activategoal, qfalse ); // t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); // if the trigger is not reachable if (!t) { continue; } activategoal->time = FloatTime() + t * 0.01 + 5; } return ent; } else if (!strcmp(classname, "func_timer")) { // just skip the func_timer continue; } // the actual button or trigger might be linked through a target_relay or target_delay else if (!strcmp(classname, "target_relay") || !strcmp(classname, "target_delay")) { if (trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[i+1], sizeof(targetname[0]))) { i++; cur_entities[i] = trap_AAS_NextBSPEntity(0); } } } #ifdef OBSTACLEDEBUG BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no valid activator for entity with target \"%s\"\n", targetname[0]); #endif return 0; } /* ================== BotGoForActivateGoal ================== */ int BotGoForActivateGoal(bot_state_t *bs, bot_activategoal_t *activategoal) { aas_entityinfo_t activateinfo; activategoal->inuse = qtrue; if (!activategoal->time) activategoal->time = FloatTime() + 10; activategoal->start_time = FloatTime(); BotEntityInfo(activategoal->goal.entitynum, &activateinfo); VectorCopy(activateinfo.origin, activategoal->origin); // if (BotPushOntoActivateGoalStack(bs, activategoal)) { // enter the activate entity AI node AIEnter_Seek_ActivateEntity(bs, "BotGoForActivateGoal"); return qtrue; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(activategoal, qtrue); return qfalse; } } /* ================== BotPrintActivateGoalInfo ================== */ void BotPrintActivateGoalInfo(bot_state_t *bs, bot_activategoal_t *activategoal, int bspent) { char netname[MAX_NETNAME]; char classname[128]; char buf[128]; ClientName(bs->client, netname, sizeof(netname)); trap_AAS_ValueForBSPEpairKey(bspent, "classname", classname, sizeof(classname)); if (activategoal->shoot) { Com_sprintf(buf, sizeof(buf), "%s: I have to shoot at a %s from %1.1f %1.1f %1.1f in area %d\n", netname, classname, activategoal->goal.origin[0], activategoal->goal.origin[1], activategoal->goal.origin[2], activategoal->goal.areanum); } else { Com_sprintf(buf, sizeof(buf), "%s: I have to activate a %s at %1.1f %1.1f %1.1f in area %d\n", netname, classname, activategoal->goal.origin[0], activategoal->goal.origin[1], activategoal->goal.origin[2], activategoal->goal.areanum); } trap_EA_Say(bs->client, buf); } /* ================== BotRandomMove ================== */ void BotRandomMove(bot_state_t *bs, bot_moveresult_t *moveresult) { vec3_t dir, angles; angles[0] = 0; angles[1] = random() * 360; angles[2] = 0; AngleVectors(angles, dir, NULL, NULL); trap_BotMoveInDirection(bs->ms, dir, 400, MOVE_WALK); moveresult->failure = qfalse; VectorCopy(dir, moveresult->movedir); } /* ================== BotAIBlocked Very basic handling of bots being blocked by other entities. Check what kind of entity is blocking the bot and try to activate it. If that's not an option then try to walk around or over the entity. Before the bot ends in this part of the AI it should predict which doors to open, which buttons to activate etc. ================== */ void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) { int movetype, bspent; vec3_t hordir, start, end, mins, maxs, sideward, angles, up = {0, 0, 1}; aas_entityinfo_t entinfo; bot_activategoal_t activategoal; // if the bot is not blocked by anything if (!moveresult->blocked) { bs->notblocked_time = FloatTime(); return; } // if stuck in a solid area if ( moveresult->type == RESULTTYPE_INSOLIDAREA ) { // move in a random direction in the hope to get out BotRandomMove(bs, moveresult); // return; } // get info for the entity that is blocking the bot BotEntityInfo(moveresult->blockentity, &entinfo); #ifdef OBSTACLEDEBUG ClientName(bs->client, netname, sizeof(netname)); BotAI_Print(PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex); #endif // OBSTACLEDEBUG // if blocked by a bsp model and the bot wants to activate it if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex) { // find the bsp entity which should be activated in order to get the blocking entity out of the way bspent = BotGetActivateGoal(bs, entinfo.number, &activategoal); if (bspent) { // if (bs->activatestack && !bs->activatestack->inuse) bs->activatestack = NULL; // if not already trying to activate this entity if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { // BotGoForActivateGoal(bs, &activategoal); } // if ontop of an obstacle or // if the bot is not in a reachability area it'll still // need some dynamic obstacle avoidance, otherwise return if (!(moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) && trap_AAS_AreaReachability(bs->areanum)) return; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(&activategoal, qtrue); } } // just some basic dynamic obstacle avoidance code hordir[0] = moveresult->movedir[0]; hordir[1] = moveresult->movedir[1]; hordir[2] = 0; // if no direction just take a random direction if (VectorNormalize(hordir) < 0.1) { VectorSet(angles, 0, 360 * random(), 0); AngleVectors(angles, hordir, NULL, NULL); } // //if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; //else movetype = MOVE_WALK; // if there's an obstacle at the bot's feet and head then // the bot might be able to crouch through VectorCopy(bs->origin, start); start[2] += 18; VectorMA(start, 5, hordir, end); VectorSet(mins, -16, -16, -24); VectorSet(maxs, 16, 16, 4); // //bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); //if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH; // get the sideward vector CrossProduct(hordir, up, sideward); // if (bs->flags & BFL_AVOIDRIGHT) VectorNegate(sideward, sideward); // try to crouch straight forward? if (movetype != MOVE_CROUCH || !trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) { // perform the movement if (!trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { // flip the avoid direction flag bs->flags ^= BFL_AVOIDRIGHT; // flip the direction // VectorNegate(sideward, sideward); VectorMA(sideward, -1, hordir, sideward); // move in the other direction trap_BotMoveInDirection(bs->ms, sideward, 400, movetype); } } // if (bs->notblocked_time < FloatTime() - 0.4) { // just reset goals and hope the bot will go into another direction? // is this still needed?? if (bs->ainode == AINode_Seek_NBG) bs->nbg_time = 0; else if (bs->ainode == AINode_Seek_LTG) bs->ltg_time = 0; } } /* ================== BotAIPredictObstacles Predict the route towards the goal and check if the bot will be blocked by certain obstacles. When the bot has obstacles on it's path the bot should figure out if they can be removed by activating certain entities. ================== */ int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal) { int modelnum, entitynum, bspent; bot_activategoal_t activategoal; aas_predictroute_t route; if (!bot_predictobstacles.integer) return qfalse; // always predict when the goal change or at regular intervals if (bs->predictobstacles_goalareanum == goal->areanum && bs->predictobstacles_time > FloatTime() - 6) { return qfalse; } bs->predictobstacles_goalareanum = goal->areanum; bs->predictobstacles_time = FloatTime(); // predict at most 100 areas or 10 seconds ahead trap_AAS_PredictRoute(&route, bs->areanum, bs->origin, goal->areanum, bs->tfl, 100, 1000, RSE_USETRAVELTYPE|RSE_ENTERCONTENTS, AREACONTENTS_MOVER, TFL_BRIDGE, 0); // if bot has to travel through an area with a mover if (route.stopevent & RSE_ENTERCONTENTS) { // if the bot will run into a mover if (route.endcontents & AREACONTENTS_MOVER) { //NOTE: this only works with bspc 2.1 or higher modelnum = (route.endcontents & AREACONTENTS_MODELNUM) >> AREACONTENTS_MODELNUMSHIFT; if (modelnum) { // entitynum = BotModelMinsMaxs(modelnum, ET_MOVER, 0, NULL, NULL); if (entitynum) { //NOTE: BotGetActivateGoal already checks if the door is open or not bspent = BotGetActivateGoal(bs, entitynum, &activategoal); if (bspent) { // if (bs->activatestack && !bs->activatestack->inuse) bs->activatestack = NULL; // if not already trying to activate this entity if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { // //BotAI_Print(PRT_MESSAGE, "blocked by mover model %d, entity %d ?\n", modelnum, entitynum); // BotGoForActivateGoal(bs, &activategoal); return qtrue; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(&activategoal, qtrue); } } } } } } else if (route.stopevent & RSE_USETRAVELTYPE) { if (route.endtravelflags & TFL_BRIDGE) { //FIXME: check if the bridge is available to travel over } } return qfalse; } /* ================== BotCheckConsoleMessages ================== */ void BotCheckConsoleMessages(bot_state_t *bs) { char botname[MAX_NETNAME], message[MAX_MESSAGE_SIZE], netname[MAX_NETNAME], *ptr; float chat_reply; int context, handle; bot_consolemessage_t m; bot_match_t match; //the name of this bot ClientName(bs->client, botname, sizeof(botname)); // while((handle = trap_BotNextConsoleMessage(bs->cs, &m)) != 0) { //if the chat state is flooded with messages the bot will read them quickly if (trap_BotNumConsoleMessages(bs->cs) < 10) { //if it is a chat message the bot needs some time to read it if (m.type == CMS_CHAT && m.time > FloatTime() - (1 + random())) break; } // ptr = m.message; //if it is a chat message then don't unify white spaces and don't //replace synonyms in the netname if (m.type == CMS_CHAT) { // if (trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { ptr = m.message + match.variables[MESSAGE].offset; } } //unify the white spaces in the message trap_UnifyWhiteSpaces(ptr); //replace synonyms in the right context context = BotSynonymContext(bs); trap_BotReplaceSynonyms(ptr, context); //if there's no match if (!BotMatchMessage(bs, m.message)) { //if it is a chat message if (m.type == CMS_CHAT && !bot_nochat.integer) { // if (!trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } //don't use eliza chats with team messages if (match.subtype & ST_TEAM) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } // trap_BotMatchVariable(&match, NETNAME, netname, sizeof(netname)); trap_BotMatchVariable(&match, MESSAGE, message, sizeof(message)); //if this is a message from the bot self if (bs->client == ClientFromName(netname)) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } //unify the message trap_UnifyWhiteSpaces(message); // trap_Cvar_Update(&bot_testrchat); if (bot_testrchat.integer) { // trap_BotLibVarSet("bot_testrchat", "1"); //if bot replies with a chat message if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, NULL, NULL, NULL, NULL, NULL, NULL, botname, netname)) { BotAI_Print(PRT_MESSAGE, "------------------------\n"); } else { BotAI_Print(PRT_MESSAGE, "**** no valid reply ****\n"); } } //if at a valid chat position and not chatting already and not in teamplay else if (bs->ainode != AINode_Stand && BotValidChatPosition(bs) && !TeamPlayIsOn()) { chat_reply = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_REPLY, 0, 1); if (random() < 1.5 / (NumBots()+1) && random() < chat_reply) { //if bot replies with a chat message if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, NULL, NULL, NULL, NULL, NULL, NULL, botname, netname)) { //remove the console message trap_BotRemoveConsoleMessage(bs->cs, handle); bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "BotCheckConsoleMessages: reply chat"); //EA_Say(bs->client, bs->cs.chatmessage); break; } } } } } //remove the console message trap_BotRemoveConsoleMessage(bs->cs, handle); } } /* ================== BotCheckEvents ================== */ void BotCheckForGrenades(bot_state_t *bs, entityState_t *state) { // if this is not a grenade if (state->eType != ET_MISSILE || state->weapon != WP_GRENADE_LAUNCHER) return; // try to avoid the grenade trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 160, AVOID_ALWAYS); } /* ================== BotCheckForProxMines ================== */ void BotCheckForProxMines(bot_state_t *bs, entityState_t *state) { // if this is not a prox mine if (state->eType != ET_MISSILE || state->weapon != WP_PROX_LAUNCHER) return; // if this prox mine is from someone on our own team if (state->generic1 == BotTeam(bs)) return; // if the bot doesn't have a weapon to deactivate the mine if (!(bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) && !(bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) && !(bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) ) { return; } // try to avoid the prox mine trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 160, AVOID_ALWAYS); // if (bs->numproxmines >= MAX_PROXMINES) return; bs->proxmines[bs->numproxmines] = state->number; bs->numproxmines++; } /* ================== BotCheckForKamikazeBody ================== */ void BotCheckForKamikazeBody(bot_state_t *bs, entityState_t *state) { // if this entity is not wearing the kamikaze if (!(state->eFlags & EF_KAMIKAZE)) return; // if this entity isn't dead if (!(state->eFlags & EF_DEAD)) return; //remember this kamikaze body bs->kamikazebody = state->number; } /* ================== BotCheckEvents ================== */ void BotCheckEvents(bot_state_t *bs, entityState_t *state) { int event; char buf[128]; aas_entityinfo_t entinfo; //NOTE: this sucks, we're accessing the gentity_t directly //but there's no other fast way to do it right now if (bs->entityeventTime[state->number] == g_entities[state->number].eventTime) { return; } bs->entityeventTime[state->number] = g_entities[state->number].eventTime; //if it's an event only entity if (state->eType > ET_EVENTS) { event = (state->eType - ET_EVENTS) & ~EV_EVENT_BITS; } else { event = state->event & ~EV_EVENT_BITS; } // switch(event) { //client obituary event case EV_OBITUARY: { int target, attacker, mod; target = state->otherEntityNum; attacker = state->otherEntityNum2; mod = state->eventParm; // if (target == bs->client) { bs->botdeathtype = mod; bs->lastkilledby = attacker; // if (target == attacker || target == ENTITYNUM_NONE || target == ENTITYNUM_WORLD) bs->botsuicide = qtrue; else bs->botsuicide = qfalse; // bs->num_deaths++; } //else if this client was killed by the bot else if (attacker == bs->client) { bs->enemydeathtype = mod; bs->lastkilledplayer = target; bs->killedenemy_time = FloatTime(); // bs->num_kills++; } else if (attacker == bs->enemy && target == attacker) { bs->enemysuicide = qtrue; } // if (gametype == GT_1FCTF) { // BotEntityInfo(target, &entinfo); if ( entinfo.powerups & ( 1 << PW_NEUTRALFLAG ) ) { if (!BotSameTeam(bs, target)) { bs->neutralflagstatus = 3; //enemy dropped the flag bs->flagstatuschanged = qtrue; } } } break; } case EV_GLOBAL_SOUND: { if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { BotAI_Print(PRT_ERROR, "EV_GLOBAL_SOUND: eventParm (%d) out of range\n", state->eventParm); break; } trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); /* if (!strcmp(buf, "sound/teamplay/flagret_red.wav")) { //red flag is returned bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; } else if (!strcmp(buf, "sound/teamplay/flagret_blu.wav")) { //blue flag is returned bs->blueflagstatus = 0; bs->flagstatuschanged = qtrue; } else*/ if (!strcmp(buf, "sound/items/kamikazerespawn.wav" )) { //the kamikaze respawned so dont avoid it BotDontAvoid(bs, "Kamikaze"); } else if (!strcmp(buf, "sound/items/poweruprespawn.wav")) { //powerup respawned... go get it BotGoForPowerups(bs); } break; } case EV_GLOBAL_TEAM_SOUND: { if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { switch(state->eventParm) { case GTS_RED_CAPTURE: bs->blueflagstatus = 0; bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_BLUE_CAPTURE: bs->blueflagstatus = 0; bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_RED_RETURN: //blue flag is returned bs->blueflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_BLUE_RETURN: //red flag is returned bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_RED_TAKEN: //blue flag is taken bs->blueflagstatus = 1; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_BLUE_TAKEN: //red flag is taken bs->redflagstatus = 1; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF } } else if (gametype == GT_1FCTF) { switch(state->eventParm) { case GTS_RED_CAPTURE: bs->neutralflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_BLUE_CAPTURE: bs->neutralflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_RED_RETURN: //flag has returned bs->neutralflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_BLUE_RETURN: //flag has returned bs->neutralflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_RED_TAKEN: bs->neutralflagstatus = BotTeam(bs) == TEAM_RED ? 2 : 1; //FIXME: check Team_TakeFlagSound in g_team.c bs->flagstatuschanged = qtrue; break; case GTS_BLUE_TAKEN: bs->neutralflagstatus = BotTeam(bs) == TEAM_BLUE ? 2 : 1; //FIXME: check Team_TakeFlagSound in g_team.c bs->flagstatuschanged = qtrue; break; } } break; } case EV_PLAYER_TELEPORT_IN: { VectorCopy(state->origin, lastteleport_origin); lastteleport_time = FloatTime(); break; } case EV_GENERAL_SOUND: { //if this sound is played on the bot if (state->number == bs->client) { if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { BotAI_Print(PRT_ERROR, "EV_GENERAL_SOUND: eventParm (%d) out of range\n", state->eventParm); break; } //check out the sound trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); //if falling into a death pit if (!strcmp(buf, "*falling1.wav")) { //if the bot has a personal teleporter if (bs->inventory[INVENTORY_TELEPORTER] > 0) { //use the holdable item trap_EA_Use(bs->client); } } } break; } case EV_FOOTSTEP: case EV_FOOTSTEP_METAL: case EV_FOOTSPLASH: case EV_FOOTWADE: case EV_SWIM: case EV_FALL_SHORT: case EV_FALL_MEDIUM: case EV_FALL_FAR: case EV_STEP_4: case EV_STEP_8: case EV_STEP_12: case EV_STEP_16: case EV_JUMP_PAD: case EV_JUMP: case EV_TAUNT: case EV_WATER_TOUCH: case EV_WATER_LEAVE: case EV_WATER_UNDER: case EV_WATER_CLEAR: case EV_ITEM_PICKUP: case EV_GLOBAL_ITEM_PICKUP: case EV_NOAMMO: case EV_CHANGE_WEAPON: case EV_FIRE_WEAPON: //FIXME: either add to sound queue or mark player as someone making noise break; case EV_USE_ITEM0: case EV_USE_ITEM1: case EV_USE_ITEM2: case EV_USE_ITEM3: case EV_USE_ITEM4: case EV_USE_ITEM5: case EV_USE_ITEM6: case EV_USE_ITEM7: case EV_USE_ITEM8: case EV_USE_ITEM9: case EV_USE_ITEM10: case EV_USE_ITEM11: case EV_USE_ITEM12: case EV_USE_ITEM13: case EV_USE_ITEM14: break; } } /* ================== BotCheckSnapshot ================== */ void BotCheckSnapshot(bot_state_t *bs) { int ent; entityState_t state; //remove all avoid spots trap_BotAddAvoidSpot(bs->ms, vec3_origin, 0, AVOID_CLEAR); //reset kamikaze body bs->kamikazebody = 0; //reset number of proxmines bs->numproxmines = 0; // ent = 0; while( ( ent = BotAI_GetSnapshotEntity( bs->client, ent, &state ) ) != -1 ) { //check the entity state for events BotCheckEvents(bs, &state); //check for grenades the bot should avoid BotCheckForGrenades(bs, &state); // //check for proximity mines which the bot should deactivate BotCheckForProxMines(bs, &state); //check for dead bodies with the kamikaze effect which should be gibbed BotCheckForKamikazeBody(bs, &state); } //check the player state for events BotAI_GetEntityState(bs->client, &state); //copy the player state events to the entity state state.event = bs->cur_ps.externalEvent; state.eventParm = bs->cur_ps.externalEventParm; // BotCheckEvents(bs, &state); } /* ================== BotCheckAir ================== */ void BotCheckAir(bot_state_t *bs) { if (bs->inventory[INVENTORY_ENVIRONMENTSUIT] <= 0) { if (trap_AAS_PointContents(bs->eye) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { return; } } bs->lastair_time = FloatTime(); } /* ================== BotAlternateRoute ================== */ bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal) { int t; // if the bot has an alternative route goal if (bs->altroutegoal.areanum) { // if (bs->reachedaltroutegoal_time) return goal; // travel time towards alternative route goal t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->altroutegoal.areanum, bs->tfl); if (t && t < 20) { //BotAI_Print(PRT_MESSAGE, "reached alternate route goal\n"); bs->reachedaltroutegoal_time = FloatTime(); } memcpy(goal, &bs->altroutegoal, sizeof(bot_goal_t)); return &bs->altroutegoal; } return goal; } /* ================== BotGetAlternateRouteGoal ================== */ int BotGetAlternateRouteGoal(bot_state_t *bs, int base) { aas_altroutegoal_t *altroutegoals; bot_goal_t *goal; int numaltroutegoals, rnd; if (base == TEAM_RED) { altroutegoals = red_altroutegoals; numaltroutegoals = red_numaltroutegoals; } else { altroutegoals = blue_altroutegoals; numaltroutegoals = blue_numaltroutegoals; } if (!numaltroutegoals) return qfalse; rnd = (float) random() * numaltroutegoals; if (rnd >= numaltroutegoals) rnd = numaltroutegoals-1; goal = &bs->altroutegoal; goal->areanum = altroutegoals[rnd].areanum; VectorCopy(altroutegoals[rnd].origin, goal->origin); VectorSet(goal->mins, -8, -8, -8); VectorSet(goal->maxs, 8, 8, 8); goal->entitynum = 0; goal->iteminfo = 0; goal->number = 0; goal->flags = 0; // bs->reachedaltroutegoal_time = 0; return qtrue; } /* ================== BotSetupAlternateRouteGoals ================== */ void BotSetupAlternativeRouteGoals(void) { if (altroutegoals_setup) return; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (trap_BotGetLevelItemGoal(-1, "Neutral Flag", &ctf_neutralflag) < 0) BotAI_Print(PRT_WARNING, "No alt routes without Neutral Flag\n"); if (ctf_neutralflag.areanum) { // red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( ctf_neutralflag.origin, ctf_neutralflag.areanum, ctf_redflag.origin, ctf_redflag.areanum, TFL_DEFAULT, red_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( ctf_neutralflag.origin, ctf_neutralflag.areanum, ctf_blueflag.origin, ctf_blueflag.areanum, TFL_DEFAULT, blue_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); } } else if (gametype == GT_1FCTF) { if (trap_BotGetLevelItemGoal(-1, "Neutral Obelisk", &neutralobelisk) < 0) BotAI_Print(PRT_WARNING, "One Flag CTF without Neutral Obelisk\n"); red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( ctf_neutralflag.origin, ctf_neutralflag.areanum, ctf_redflag.origin, ctf_redflag.areanum, TFL_DEFAULT, red_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( ctf_neutralflag.origin, ctf_neutralflag.areanum, ctf_blueflag.origin, ctf_blueflag.areanum, TFL_DEFAULT, blue_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); } else if (gametype == GT_OBELISK) { if (trap_BotGetLevelItemGoal(-1, "Neutral Obelisk", &neutralobelisk) < 0) BotAI_Print(PRT_WARNING, "Obelisk without neutral obelisk\n"); // red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( neutralobelisk.origin, neutralobelisk.areanum, redobelisk.origin, redobelisk.areanum, TFL_DEFAULT, red_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( neutralobelisk.origin, neutralobelisk.areanum, blueobelisk.origin, blueobelisk.areanum, TFL_DEFAULT, blue_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); } else if (gametype == GT_HARVESTER) { if (untrap_BotGetLevelItemGoal(-1, "Neutral Obelisk", &neutralobelisk) < 0) BotAI_Print(PRT_WARNING, "Harvester without neutral obelisk\n"); // red_numaltroutegoals = trap_AAS_AlternativeRouteGoals( neutralobelisk.origin, neutralobelisk.areanum, redobelisk.origin, redobelisk.areanum, TFL_DEFAULT, red_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); blue_numaltroutegoals = trap_AAS_AlternativeRouteGoals( neutralobelisk.origin, neutralobelisk.areanum, blueobelisk.origin, blueobelisk.areanum, TFL_DEFAULT, blue_altroutegoals, MAX_ALTROUTEGOALS, ALTROUTEGOAL_CLUSTERPORTALS| ALTROUTEGOAL_VIEWPORTALS); } altroutegoals_setup = qtrue; } /* ================== BotDeathmatchAI ================== */ void BotDeathmatchAI(bot_state_t *bs, float thinktime) { char gender[144], name[144], buf[144]; char userinfo[MAX_INFO_STRING]; int i; //if the bot has just been setup if (bs->setupcount > 0) { bs->setupcount--; if (bs->setupcount > 0) return; //get the gender characteristic trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); //set the bot gender trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "sex", gender); trap_SetUserinfo(bs->client, userinfo); //set the team if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT ) { Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); trap_EA_Command(bs->client, buf); } //set the chat gender if (gender[0] == 'm') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else if (gender[0] == 'f') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); //set the chat name ClientName(bs->client, name, sizeof(name)); trap_BotSetChatName(bs->cs, name, bs->client); // bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; // bs->setupcount = 0; // BotSetupAlternativeRouteGoals(); } //no ideal view set bs->flags &= ~BFL_IDEALVIEWSET; // if (!BotIntermission(bs)) { //set the teleport time BotSetTeleportTime(bs); //update some inventory values BotUpdateInventory(bs); //check out the snapshot BotCheckSnapshot(bs); //check for air BotCheckAir(bs); } //check the console messages BotCheckConsoleMessages(bs); //if not in the intermission and not in observer mode if (!BotIntermission(bs) && !BotIsObserver(bs)) { //do team AI BotTeamAI(bs); } //if the bot has no ai node if (!bs->ainode) { AIEnter_Seek_LTG(bs, "BotDeathmatchAI: no ai node"); } //if the bot entered the game less than 8 seconds ago if (!bs->entergamechat && bs->entergame_time > FloatTime() - 8) { if (BotChat_EnterGame(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "BotDeathmatchAI: chat enter game"); } bs->entergamechat = qtrue; } //reset the node switches from the previous frame BotResetNodeSwitches(); //execute AI nodes for (i = 0; i < MAX_NODESWITCHES; i++) { if (bs->ainode(bs)) break; } //if the bot removed itself :) if (!bs->inuse) return; //if the bot executed too many AI nodes //Sago: FIXME - Outcommented this test... this is wrong #ifdef DEBUG if (i >= MAX_NODESWITCHES) { trap_BotDumpGoalStack(bs->gs); trap_BotDumpAvoidGoals(bs->gs); BotDumpNodeSwitches(bs); ClientName(bs->client, name, sizeof(name)); BotAI_Print(PRT_ERROR, "%s at %1.1f switched more than %d AI nodes\n", name, FloatTime(), MAX_NODESWITCHES); } #endif // bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; } /* ================== BotSetEntityNumForGoalWithModel ================== */ void BotSetEntityNumForGoalWithModel(bot_goal_t *goal, int eType, char *modelname) { gentity_t *ent; int i, modelindex; vec3_t dir; modelindex = G_ModelIndex( modelname ); ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( eType && ent->s.eType != eType) { continue; } if (ent->s.modelindex != modelindex) { continue; } VectorSubtract(goal->origin, ent->s.origin, dir); if (VectorLengthSquared(dir) < Square(10)) { goal->entitynum = i; return; } } } /* ================== BotSetEntityNumForGoal ================== */ void BotSetEntityNumForGoal(bot_goal_t *goal, char *classname) { gentity_t *ent; int i; vec3_t dir; ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( !Q_stricmp(ent->classname, classname) ) { continue; } VectorSubtract(goal->origin, ent->s.origin, dir); if (VectorLengthSquared(dir) < Square(10)) { goal->entitynum = i; return; } } } /* ================== BotGoalForBSPEntity ================== */ int BotGoalForBSPEntity( char *classname, bot_goal_t *goal ) { char value[MAX_INFO_STRING]; vec3_t origin, start, end; int ent, numareas, areas[10]; memset(goal, 0, sizeof(bot_goal_t)); for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", value, sizeof(value))) continue; if (!strcmp(value, classname)) { if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) return qfalse; VectorCopy(origin, goal->origin); VectorCopy(origin, start); start[2] -= 32; VectorCopy(origin, end); end[2] += 32; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); if (!numareas) return qfalse; goal->areanum = areas[0]; return qtrue; } } return qfalse; } /* ================== BotSetupDeathmatchAI ================== */ void BotSetupDeathmatchAI(void) { int ent, modelnum,i; char model[128]; gametype = trap_Cvar_VariableIntegerValue("g_gametype"); maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0); trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0); trap_Cvar_Register(&bot_fastchat, "bot_fastchat", "0", 0); trap_Cvar_Register(&bot_nochat, "bot_nochat", "0", 0); trap_Cvar_Register(&bot_testrchat, "bot_testrchat", "0", 0); trap_Cvar_Register(&bot_challenge, "bot_challenge", "0", 0); trap_Cvar_Register(&bot_predictobstacles, "bot_predictobstacles", "1", 0); trap_Cvar_Register(&g_spSkill, "g_spSkill", "2", 0); // if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (untrap_BotGetLevelItemGoal(-1, "Red Flag", &ctf_redflag) < 0) BotAI_Print(PRT_WARNING, "CTF without Red Flag\n"); if (untrap_BotGetLevelItemGoal(-1, "Blue Flag", &ctf_blueflag) < 0) BotAI_Print(PRT_WARNING, "CTF without Blue Flag\n"); } else if (gametype == GT_DOUBLE_D) { if (untrap_BotGetLevelItemGoal(-1, "Red Flag", &ctf_redflag) < 0) BotAI_Print(PRT_WARNING, "DD without Point A\n"); if (untrap_BotGetLevelItemGoal(-1, "Blue Flag", &ctf_blueflag) < 0) BotAI_Print(PRT_WARNING, "DD without Point B\n"); } else if (gametype == GT_DOMINATION) { ent = untrap_BotGetLevelItemGoal(-1, "Domination point", &dom_points_bot[0]); if(ent < 0) BotAI_Print(PRT_WARNING, "Domination without a single domination point\n"); else BotSetEntityNumForGoal(&dom_points_bot[0], va("domination_point%i",0) ); for(i=1;i max_bspmodelindex) max_bspmodelindex = modelnum; } } //initialize the waypoint heap BotInitWaypoints(); } /* ================== BotShutdownDeathmatchAI ================== */ void BotShutdownDeathmatchAI(void) { altroutegoals_setup = qfalse; } openarena_0.8.8.orig/code/game/ai_team.h0000644000175000017500000000302311656310264016617 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_team.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ void BotTeamAI(bot_state_t *bs); int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate); void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference); void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat); void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat); openarena_0.8.8.orig/code/game/ai_vcmd.h0000644000175000017500000000253311656310264016627 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_vcmd.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_vcmd.c $ * *****************************************************************************/ int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voicechat); void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode); openarena_0.8.8.orig/code/game/g_public.h0000644000175000017500000003406211656310264017013 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_public.h -- game module information visible to server #define GAME_API_VERSION 8 // entity->svFlags // the server does not know how to interpret most of the values // in entityStates (level eType), so the game must explicitly flag // special server behaviors #define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects // TTimo // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551 #define SVF_CLIENTMASK 0x00000002 #define SVF_BOT 0x00000008 // set if the entity is a bot #define SVF_BROADCAST 0x00000020 // send to all connected clients #define SVF_PORTAL 0x00000040 // merge a second pvs at origin2 into snapshots #define SVF_USE_CURRENT_ORIGIN 0x00000080 // entity->r.currentOrigin instead of entity->s.origin // for link position (missiles and movers) #define SVF_SINGLECLIENT 0x00000100 // only send to a single client (entityShared_t->singleClient) #define SVF_NOSERVERINFO 0x00000200 // don't send CS_SERVERINFO updates to this client // so that it can be updated for ping tools without // lagging clients #define SVF_CAPSULE 0x00000400 // use capsule for collision detection instead of bbox #define SVF_NOTSINGLECLIENT 0x00000800 // send entity to everyone but one client // (entityShared_t->singleClient) //=============================================================== typedef struct { entityState_t s; // communicated by server to clients qboolean linked; // qfalse if not in any good cluster int linkcount; int svFlags; // SVF_NOCLIENT, SVF_BROADCAST, etc // only send to this client when SVF_SINGLECLIENT is set // if SVF_CLIENTMASK is set, use bitmask for clients to send to (maxclients must be <= 32, up to the mod to enforce this) int singleClient; qboolean bmodel; // if false, assume an explicit mins / maxs bounding box // only set by trap_SetBrushModel vec3_t mins, maxs; int contents; // CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc // a non-solid entity should set to 0 vec3_t absmin, absmax; // derived from mins/maxs and origin + rotation // currentOrigin will be used for all collision detection and world linking. // it will not necessarily be the same as the trajectory evaluation for the current // time, because each entity must be moved one at a time after time is advanced // to avoid simultanious collision issues vec3_t currentOrigin; vec3_t currentAngles; // when a trace call is made and passEntityNum != ENTITYNUM_NONE, // an ent will be excluded from testing if: // ent->s.number == passEntityNum (don't interact with self) // ent->s.ownerNum = passEntityNum (don't interact with your own missiles) // entity[ent->s.ownerNum].ownerNum = passEntityNum (don't interact with other missiles from owner) int ownerNum; } entityShared_t; // the server looks at a sharedEntity, which is the start of the game's gentity_t structure typedef struct { entityState_t s; // communicated by server to clients entityShared_t r; // shared by both the server system and game } sharedEntity_t; //=============================================================== // // system traps provided by the main engine // typedef enum { //============== general Quake services ================== G_PRINT, // ( const char *string ); // print message on the local console G_ERROR, // ( const char *string ); // abort the game G_MILLISECONDS, // ( void ); // get current time for profiling reasons // this should NOT be used for any game related tasks, // because it is not journaled // console variable interaction G_CVAR_REGISTER, // ( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); G_CVAR_UPDATE, // ( vmCvar_t *vmCvar ); G_CVAR_SET, // ( const char *var_name, const char *value ); G_CVAR_VARIABLE_INTEGER_VALUE, // ( const char *var_name ); G_CVAR_VARIABLE_STRING_BUFFER, // ( const char *var_name, char *buffer, int bufsize ); G_ARGC, // ( void ); // ClientCommand and ServerCommand parameter access G_ARGV, // ( int n, char *buffer, int bufferLength ); G_FS_FOPEN_FILE, // ( const char *qpath, fileHandle_t *file, fsMode_t mode ); G_FS_READ, // ( void *buffer, int len, fileHandle_t f ); G_FS_WRITE, // ( const void *buffer, int len, fileHandle_t f ); G_FS_FCLOSE_FILE, // ( fileHandle_t f ); G_SEND_CONSOLE_COMMAND, // ( const char *text ); // add commands to the console as if they were typed in // for map changing, etc //=========== server specific functionality ============= G_LOCATE_GAME_DATA, // ( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t, // playerState_t *clients, int sizeofGameClient ); // the game needs to let the server system know where and how big the gentities // are, so it can look at them directly without going through an interface G_DROP_CLIENT, // ( int clientNum, const char *reason ); // kick a client off the server with a message G_SEND_SERVER_COMMAND, // ( int clientNum, const char *fmt, ... ); // reliably sends a command string to be interpreted by the given // client. If clientNum is -1, it will be sent to all clients G_SET_CONFIGSTRING, // ( int num, const char *string ); // config strings hold all the index strings, and various other information // that is reliably communicated to all clients // All of the current configstrings are sent to clients when // they connect, and changes are sent to all connected clients. // All confgstrings are cleared at each level start. G_GET_CONFIGSTRING, // ( int num, char *buffer, int bufferSize ); G_GET_USERINFO, // ( int num, char *buffer, int bufferSize ); // userinfo strings are maintained by the server system, so they // are persistant across level loads, while all other game visible // data is completely reset G_SET_USERINFO, // ( int num, const char *buffer ); G_GET_SERVERINFO, // ( char *buffer, int bufferSize ); // the serverinfo info string has all the cvars visible to server browsers G_SET_BRUSH_MODEL, // ( gentity_t *ent, const char *name ); // sets mins and maxs based on the brushmodel name G_TRACE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask ); // collision detection against all linked entities G_POINT_CONTENTS, // ( const vec3_t point, int passEntityNum ); // point contents against all linked entities G_IN_PVS, // ( const vec3_t p1, const vec3_t p2 ); G_IN_PVS_IGNORE_PORTALS, // ( const vec3_t p1, const vec3_t p2 ); G_ADJUST_AREA_PORTAL_STATE, // ( gentity_t *ent, qboolean open ); G_AREAS_CONNECTED, // ( int area1, int area2 ); G_LINKENTITY, // ( gentity_t *ent ); // an entity will never be sent to a client or used for collision // if it is not passed to linkentity. If the size, position, or // solidity changes, it must be relinked. G_UNLINKENTITY, // ( gentity_t *ent ); // call before removing an interactive entity G_ENTITIES_IN_BOX, // ( const vec3_t mins, const vec3_t maxs, gentity_t **list, int maxcount ); // EntitiesInBox will return brush models based on their bounding box, // so exact determination must still be done with EntityContact G_ENTITY_CONTACT, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent ); // perform an exact check against inline brush models of non-square shape // access for bots to get and free a server client (FIXME?) G_BOT_ALLOCATE_CLIENT, // ( void ); G_BOT_FREE_CLIENT, // ( int clientNum ); G_GET_USERCMD, // ( int clientNum, usercmd_t *cmd ) G_GET_ENTITY_TOKEN, // qboolean ( char *buffer, int bufferSize ) // Retrieves the next string token from the entity spawn text, returning // false when all tokens have been parsed. // This should only be done at GAME_INIT time. G_FS_GETFILELIST, G_DEBUG_POLYGON_CREATE, G_DEBUG_POLYGON_DELETE, G_REAL_TIME, G_SNAPVECTOR, G_TRACECAPSULE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask ); G_ENTITY_CONTACTCAPSULE, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent ); // 1.32 G_FS_SEEK, BOTLIB_SETUP = 200, // ( void ); BOTLIB_SHUTDOWN, // ( void ); BOTLIB_LIBVAR_SET, BOTLIB_LIBVAR_GET, BOTLIB_PC_ADD_GLOBAL_DEFINE, BOTLIB_START_FRAME, BOTLIB_LOAD_MAP, BOTLIB_UPDATENTITY, BOTLIB_TEST, BOTLIB_GET_SNAPSHOT_ENTITY, // ( int client, int ent ); BOTLIB_GET_CONSOLE_MESSAGE, // ( int client, char *message, int size ); BOTLIB_USER_COMMAND, // ( int client, usercmd_t *ucmd ); BOTLIB_AAS_ENABLE_ROUTING_AREA = 300, BOTLIB_AAS_BBOX_AREAS, BOTLIB_AAS_AREA_INFO, BOTLIB_AAS_ENTITY_INFO, BOTLIB_AAS_INITIALIZED, BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX, BOTLIB_AAS_TIME, BOTLIB_AAS_POINT_AREA_NUM, BOTLIB_AAS_TRACE_AREAS, BOTLIB_AAS_POINT_CONTENTS, BOTLIB_AAS_NEXT_BSP_ENTITY, BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY, BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY, BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY, BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY, BOTLIB_AAS_AREA_REACHABILITY, BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA, BOTLIB_AAS_SWIMMING, BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT, BOTLIB_EA_SAY = 400, BOTLIB_EA_SAY_TEAM, BOTLIB_EA_COMMAND, BOTLIB_EA_ACTION, BOTLIB_EA_GESTURE, BOTLIB_EA_TALK, BOTLIB_EA_ATTACK, BOTLIB_EA_USE, BOTLIB_EA_RESPAWN, BOTLIB_EA_CROUCH, BOTLIB_EA_MOVE_UP, BOTLIB_EA_MOVE_DOWN, BOTLIB_EA_MOVE_FORWARD, BOTLIB_EA_MOVE_BACK, BOTLIB_EA_MOVE_LEFT, BOTLIB_EA_MOVE_RIGHT, BOTLIB_EA_SELECT_WEAPON, BOTLIB_EA_JUMP, BOTLIB_EA_DELAYED_JUMP, BOTLIB_EA_MOVE, BOTLIB_EA_VIEW, BOTLIB_EA_END_REGULAR, BOTLIB_EA_GET_INPUT, BOTLIB_EA_RESET_INPUT, BOTLIB_AI_LOAD_CHARACTER = 500, BOTLIB_AI_FREE_CHARACTER, BOTLIB_AI_CHARACTERISTIC_FLOAT, BOTLIB_AI_CHARACTERISTIC_BFLOAT, BOTLIB_AI_CHARACTERISTIC_INTEGER, BOTLIB_AI_CHARACTERISTIC_BINTEGER, BOTLIB_AI_CHARACTERISTIC_STRING, BOTLIB_AI_ALLOC_CHAT_STATE, BOTLIB_AI_FREE_CHAT_STATE, BOTLIB_AI_QUEUE_CONSOLE_MESSAGE, BOTLIB_AI_REMOVE_CONSOLE_MESSAGE, BOTLIB_AI_NEXT_CONSOLE_MESSAGE, BOTLIB_AI_NUM_CONSOLE_MESSAGE, BOTLIB_AI_INITIAL_CHAT, BOTLIB_AI_REPLY_CHAT, BOTLIB_AI_CHAT_LENGTH, BOTLIB_AI_ENTER_CHAT, BOTLIB_AI_STRING_CONTAINS, BOTLIB_AI_FIND_MATCH, BOTLIB_AI_MATCH_VARIABLE, BOTLIB_AI_UNIFY_WHITE_SPACES, BOTLIB_AI_REPLACE_SYNONYMS, BOTLIB_AI_LOAD_CHAT_FILE, BOTLIB_AI_SET_CHAT_GENDER, BOTLIB_AI_SET_CHAT_NAME, BOTLIB_AI_RESET_GOAL_STATE, BOTLIB_AI_RESET_AVOID_GOALS, BOTLIB_AI_PUSH_GOAL, BOTLIB_AI_POP_GOAL, BOTLIB_AI_EMPTY_GOAL_STACK, BOTLIB_AI_DUMP_AVOID_GOALS, BOTLIB_AI_DUMP_GOAL_STACK, BOTLIB_AI_GOAL_NAME, BOTLIB_AI_GET_TOP_GOAL, BOTLIB_AI_GET_SECOND_GOAL, BOTLIB_AI_CHOOSE_LTG_ITEM, BOTLIB_AI_CHOOSE_NBG_ITEM, BOTLIB_AI_TOUCHING_GOAL, BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE, BOTLIB_AI_GET_LEVEL_ITEM_GOAL, BOTLIB_AI_AVOID_GOAL_TIME, BOTLIB_AI_INIT_LEVEL_ITEMS, BOTLIB_AI_UPDATE_ENTITY_ITEMS, BOTLIB_AI_LOAD_ITEM_WEIGHTS, BOTLIB_AI_FREE_ITEM_WEIGHTS, BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC, BOTLIB_AI_ALLOC_GOAL_STATE, BOTLIB_AI_FREE_GOAL_STATE, BOTLIB_AI_RESET_MOVE_STATE, BOTLIB_AI_MOVE_TO_GOAL, BOTLIB_AI_MOVE_IN_DIRECTION, BOTLIB_AI_RESET_AVOID_REACH, BOTLIB_AI_RESET_LAST_AVOID_REACH, BOTLIB_AI_REACHABILITY_AREA, BOTLIB_AI_MOVEMENT_VIEW_TARGET, BOTLIB_AI_ALLOC_MOVE_STATE, BOTLIB_AI_FREE_MOVE_STATE, BOTLIB_AI_INIT_MOVE_STATE, BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON, BOTLIB_AI_GET_WEAPON_INFO, BOTLIB_AI_LOAD_WEAPON_WEIGHTS, BOTLIB_AI_ALLOC_WEAPON_STATE, BOTLIB_AI_FREE_WEAPON_STATE, BOTLIB_AI_RESET_WEAPON_STATE, BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION, BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC, BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC, BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL, BOTLIB_AI_GET_MAP_LOCATION_GOAL, BOTLIB_AI_NUM_INITIAL_CHATS, BOTLIB_AI_GET_CHAT_MESSAGE, BOTLIB_AI_REMOVE_FROM_AVOID_GOALS, BOTLIB_AI_PREDICT_VISIBLE_POSITION, BOTLIB_AI_SET_AVOID_GOAL_TIME, BOTLIB_AI_ADD_AVOID_SPOT, BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL, BOTLIB_AAS_PREDICT_ROUTE, BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX, BOTLIB_PC_LOAD_SOURCE, BOTLIB_PC_FREE_SOURCE, BOTLIB_PC_READ_TOKEN, BOTLIB_PC_SOURCE_FILE_AND_LINE } gameImport_t; // // functions exported by the game subsystem // typedef enum { GAME_INIT, // ( int levelTime, int randomSeed, int restart ); // init and shutdown will be called every single level // The game should call G_GET_ENTITY_TOKEN to parse through all the // entity configuration text and spawn gentities. GAME_SHUTDOWN, // (void); GAME_CLIENT_CONNECT, // ( int clientNum, qboolean firstTime, qboolean isBot ); // return NULL if the client is allowed to connect, otherwise return // a text string with the reason for denial GAME_CLIENT_BEGIN, // ( int clientNum ); GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum ); GAME_CLIENT_DISCONNECT, // ( int clientNum ); GAME_CLIENT_COMMAND, // ( int clientNum ); GAME_CLIENT_THINK, // ( int clientNum ); GAME_RUN_FRAME, // ( int levelTime ); GAME_CONSOLE_COMMAND, // ( void ); // ConsoleCommand will be called when a command has been issued // that is not recognized as a builtin function. // The game can issue trap_argc() / trap_argv() commands to get the command // and parameters. Return qfalse if the game doesn't recognize it as a command. BOTAI_START_FRAME // ( int time ); } gameExport_t; openarena_0.8.8.orig/code/game/ai_dmnet.h0000644000175000017500000000454411656310264017011 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmnet.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ #define MAX_NODESWITCHES 50 void AIEnter_Intermission(bot_state_t *bs, char *s); void AIEnter_Observer(bot_state_t *bs, char *s); void AIEnter_Respawn(bot_state_t *bs, char *s); void AIEnter_Stand(bot_state_t *bs, char *s); void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s); void AIEnter_Seek_NBG(bot_state_t *bs, char *s); void AIEnter_Seek_LTG(bot_state_t *bs, char *s); void AIEnter_Seek_Camp(bot_state_t *bs, char *s); void AIEnter_Battle_Fight(bot_state_t *bs, char *s); void AIEnter_Battle_Chase(bot_state_t *bs, char *s); void AIEnter_Battle_Retreat(bot_state_t *bs, char *s); void AIEnter_Battle_NBG(bot_state_t *bs, char *s); int AINode_Intermission(bot_state_t *bs); int AINode_Observer(bot_state_t *bs); int AINode_Respawn(bot_state_t *bs); int AINode_Stand(bot_state_t *bs); int AINode_Seek_ActivateEntity(bot_state_t *bs); int AINode_Seek_NBG(bot_state_t *bs); int AINode_Seek_LTG(bot_state_t *bs); int AINode_Battle_Fight(bot_state_t *bs); int AINode_Battle_Chase(bot_state_t *bs); int AINode_Battle_Retreat(bot_state_t *bs); int AINode_Battle_NBG(bot_state_t *bs); void BotResetNodeSwitches(void); void BotDumpNodeSwitches(bot_state_t *bs); openarena_0.8.8.orig/code/game/g_weapon.c0000644000175000017500000010051311656310264017014 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_weapon.c // perform the server side effects of a weapon firing #include "g_local.h" static float s_quadFactor; static vec3_t forward, right, up; static vec3_t muzzle; #define NUM_NAILSHOTS 15 /* ================ G_BounceProjectile ================ */ void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) { vec3_t v, newv; float dot; VectorSubtract( impact, start, v ); dot = DotProduct( v, dir ); VectorMA( v, -2*dot, dir, newv ); VectorNormalize(newv); VectorMA(impact, 8192, newv, endout); } /* ====================================================================== GAUNTLET ====================================================================== */ void Weapon_Gauntlet( gentity_t *ent ) { } /* =============== CheckGauntletAttack =============== */ qboolean CheckGauntletAttack( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; int damage; // set aiming directions AngleVectors (ent->client->ps.viewangles, forward, right, up); CalcMuzzlePoint ( ent, forward, right, up, muzzle ); VectorMA (muzzle, 32, forward, end); trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return qfalse; } if ( ent->client->noclip ) { return qfalse; } traceEnt = &g_entities[ tr.entityNum ]; // send blood impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; } if ( !traceEnt->takedamage) { return qfalse; } if (ent->client->ps.powerups[PW_QUAD] ) { G_AddEvent( ent, EV_POWERUP_QUAD, 0 ); s_quadFactor = g_quadfactor.value; } else { s_quadFactor = 1; } if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) { s_quadFactor *= 2; } if(g_instantgib.integer) damage = 500; //High damage in instant gib (normally enough to gib) else damage = 50 * s_quadFactor; G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_GAUNTLET ); return qtrue; } /* ====================================================================== MACHINEGUN ====================================================================== */ /* ====================== SnapVectorTowards Round a vector to integers for more efficient network transmission, but make sure that it rounds towards a given point rather than blindly truncating. This prevents it from truncating into a wall. ====================== */ //unlagged - attack prediction #3 // moved to q_shared.c /* void SnapVectorTowards( vec3_t v, vec3_t to ) { int i; for ( i = 0 ; i < 3 ; i++ ) { if ( to[i] <= v[i] ) { v[i] = floor(v[i]); } else { v[i] = ceil(v[i]); } } } */ //unlagged - attack prediction #3 //unlagged - attack prediction #3 // moved from g_weapon.c /* ====================== SnapVectorTowards Round a vector to integers for more efficient network transmission, but make sure that it rounds towards a given point rather than blindly truncating. This prevents it from truncating into a wall. ====================== */ void SnapVectorTowards( vec3_t v, vec3_t to ) { int i; for ( i = 0 ; i < 3 ; i++ ) { if ( to[i] <= v[i] ) { v[i] = (int)v[i]; } else { v[i] = (int)v[i] + 1; } } } //unlagged - attack prediction #3 #define CHAINGUN_SPREAD 600.0 #define MACHINEGUN_SPREAD 200 #define MACHINEGUN_DAMAGE 7 #define MACHINEGUN_TEAM_DAMAGE 5 // wimpier MG in teamplay void Bullet_Fire (gentity_t *ent, float spread, int damage ) { trace_t tr; vec3_t end; vec3_t impactpoint, bouncedir; float r; float u; gentity_t *tent; gentity_t *traceEnt; int i, passent; //unlagged - attack prediction #2 // we have to use something now that the client knows in advance int seed = ent->client->attackTime % 256; //unlagged - attack prediction #2 damage *= s_quadFactor; //unlagged - attack prediction #2 // this has to match what's on the client /* r = random() * M_PI * 2.0f; u = sin(r) * crandom() * spread * 16; r = cos(r) * crandom() * spread * 16; */ r = Q_random(&seed) * M_PI * 2.0f; u = sin(r) * Q_crandom(&seed) * spread * 16; r = cos(r) * Q_crandom(&seed) * spread * 16; //unlagged - attack prediction #2 VectorMA (muzzle, 8192*16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); passent = ent->s.number; for (i = 0; i < 10; i++) { //unlagged - backward reconciliation #2 // backward-reconcile the other clients G_DoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT); //unlagged - backward reconciliation #2 // put them back G_UndoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzle ); // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; //unlagged - attack prediction #2 // we need the client number to determine whether or not to // suppress this event tent->s.clientNum = ent->s.clientNum; //unlagged - attack prediction #2 if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; } } else { tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL ); tent->s.eventParm = DirToByte( tr.plane.normal ); //unlagged - attack prediction #2 // we need the client number to determine whether or not to // suppress this event tent->s.clientNum = ent->s.clientNum; //unlagged - attack prediction #2 } tent->s.otherEntityNum = ent->s.number; if ( traceEnt->takedamage) { if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, muzzle ); // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, muzzle ); passent = traceEnt->s.number; } continue; } else { if(spread == CHAINGUN_SPREAD) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_CHAINGUN); ent->client->accuracy[WP_CHAINGUN][1]++; } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_MACHINEGUN); ent->client->accuracy[WP_MACHINEGUN][1]++; } } } break; } } /* ====================================================================== BFG ====================================================================== */ void BFG_Fire ( gentity_t *ent ) { gentity_t *m; m = fire_bfg (ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } /* ====================================================================== SHOTGUN ====================================================================== */ // DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because // client predicts same spreads #define DEFAULT_SHOTGUN_DAMAGE 10 qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) { trace_t tr; int damage, i, passent; gentity_t *traceEnt; vec3_t impactpoint, bouncedir; vec3_t tr_start, tr_end; passent = ent->s.number; VectorCopy( start, tr_start ); VectorCopy( end, tr_end ); for (i = 0; i < 10; i++) { trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT); traceEnt = &g_entities[ tr.entityNum ]; // send bullet impact if ( tr.surfaceFlags & SURF_NOIMPACT ) { return qfalse; } if ( traceEnt->takedamage) { damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor; if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end ); VectorCopy( impactpoint, tr_start ); // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, tr_start ); passent = traceEnt->s.number; } continue; } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN); if( LogAccuracyHit( traceEnt, ent ) ) { return qtrue; } } } return qfalse; } return qfalse; } // this should match CG_ShotgunPattern void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) { int i; float r, u; vec3_t end; vec3_t forward, right, up; int oldScore; qboolean hitClient = qfalse; //unlagged - attack prediction #2 // use this for testing //Com_Printf( "Server seed: %d\n", seed ); //unlagged - attack prediction #2 // derive the right and up vectors from the forward vector, because // the client won't have any other information VectorNormalize2( origin2, forward ); PerpendicularVector( right, forward ); CrossProduct( forward, right, up ); oldScore = ent->client->ps.persistant[PERS_SCORE]; //unlagged - backward reconciliation #2 // backward-reconcile the other clients G_DoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 // generate the "random" spread pattern for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) { r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; VectorMA( origin, 8192 * 16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if( ShotgunPellet( origin, end, ent ) && !hitClient ) { hitClient = qtrue; ent->client->accuracy_hits++; } } if( hitClient ) ent->client->accuracy[WP_SHOTGUN][1]++; //unlagged - backward reconciliation #2 // put them back G_UndoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 } void weapon_supershotgun_fire (gentity_t *ent) { gentity_t *tent; // send shotgun blast tent = G_TempEntity( muzzle, EV_SHOTGUN ); VectorScale( forward, 4096, tent->s.origin2 ); SnapVector( tent->s.origin2 ); //Sago: This sound like a bad idea... //unlagged - attack prediction #2 // this has to be something the client can predict now //tent->s.eventParm = rand() & 255; // seed for spread pattern tent->s.eventParm = ent->client->attackTime % 256; // seed for spread pattern //unlagged - attack prediction #2 tent->s.otherEntityNum = ent->s.number; ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent ); } /* ====================================================================== GRENADE LAUNCHER ====================================================================== */ void weapon_grenadelauncher_fire (gentity_t *ent) { gentity_t *m; // extra vertical velocity forward[2] += 0.2f; VectorNormalize( forward ); m = fire_grenade (ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } /* ====================================================================== ROCKET ====================================================================== */ void Weapon_RocketLauncher_Fire (gentity_t *ent) { gentity_t *m; m = fire_rocket (ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } /* ====================================================================== PLASMA GUN ====================================================================== */ void Weapon_Plasmagun_Fire (gentity_t *ent) { gentity_t *m; m = fire_plasma (ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } /* ====================================================================== RAILGUN ====================================================================== */ /* ================= weapon_railgun_fire ================= */ #define MAX_RAIL_HITS 4 void weapon_railgun_fire (gentity_t *ent) { vec3_t end; vec3_t impactpoint, bouncedir; trace_t trace; gentity_t *tent; gentity_t *traceEnt; int damage; int i; int hits; int unlinked; int passent; gentity_t *unlinkedEntities[MAX_RAIL_HITS]; damage = 100 * s_quadFactor; if(g_instantgib.integer) damage = 800; VectorMA (muzzle, 8192, forward, end); //unlagged - backward reconciliation #2 // backward-reconcile the other clients G_DoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 // trace only against the solids, so the railgun will go through people unlinked = 0; hits = 0; passent = ent->s.number; do { trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT ); if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) { break; } traceEnt = &g_entities[ trace.entityNum ]; if ( traceEnt->takedamage ) { if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) { G_BounceProjectile( muzzle, impactpoint, bouncedir, end ); // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.clientNum = ent->s.clientNum; VectorCopy( muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); tent->s.eventParm = 255; // don't make the explosion at the end // VectorCopy( impactpoint, muzzle ); // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } } else { if( LogAccuracyHit( traceEnt, ent ) ) { hits++; } G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); } } if ( trace.contents & CONTENTS_SOLID ) { break; // we hit something solid enough to stop the beam } // unlink this entity, so the next trace will go past it trap_UnlinkEntity( traceEnt ); unlinkedEntities[unlinked] = traceEnt; unlinked++; } while ( unlinked < MAX_RAIL_HITS ); //unlagged - backward reconciliation #2 // put them back G_UndoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 // link back in any entities we unlinked for ( i = 0 ; i < unlinked ; i++ ) { trap_LinkEntity( unlinkedEntities[i] ); } // the final trace endpos will be the terminal point of the rail trail // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.clientNum = ent->s.clientNum; VectorCopy( muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); // no explosion at end if SURF_NOIMPACT, but still make the trail if ( trace.surfaceFlags & SURF_NOIMPACT ) { tent->s.eventParm = 255; // don't make the explosion at the end } else { tent->s.eventParm = DirToByte( trace.plane.normal ); } tent->s.clientNum = ent->s.clientNum; // give the shooter a reward sound if they have made two railgun hits in a row if ( hits == 0 ) { // complete miss ent->client->accurateCount = 0; } else { // check for "impressive" reward sound ent->client->accurateCount += hits; if ( ent->client->accurateCount >= 2 ) { ent->client->accurateCount -= 2; ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", ent->client->ps.clientNum, 2, ent->client->pers.netname, "IMPRESSIVE" ); if(!level.hadBots) //There has not been any bots ChallengeMessage(ent,AWARD_IMPRESSIVE); // add the sprite over the player's head ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE; ent->client->rewardTime = level.time + REWARD_SPRITE_TIME; } ent->client->accuracy_hits++; ent->client->accuracy[WP_RAILGUN][1]++; } } /* ====================================================================== GRAPPLING HOOK ====================================================================== */ void Weapon_GrapplingHook_Fire (gentity_t *ent) { if (!ent->client->fireHeld && !ent->client->hook) fire_grapple (ent, muzzle, forward); ent->client->fireHeld = qtrue; } void Weapon_HookFree (gentity_t *ent) { ent->parent->client->hook = NULL; ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL; G_FreeEntity( ent ); } void Weapon_HookThink (gentity_t *ent) { if (ent->enemy) { vec3_t v, oldorigin; VectorCopy(ent->r.currentOrigin, oldorigin); v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5; v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5; v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5; SnapVectorTowards( v, oldorigin ); // save net bandwidth G_SetOrigin( ent, v ); } VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); } /* ====================================================================== LIGHTNING GUN ====================================================================== */ void Weapon_LightningFire( gentity_t *ent ) { trace_t tr; vec3_t end; vec3_t impactpoint, bouncedir; gentity_t *traceEnt, *tent; int damage, i, passent; damage = 8 * s_quadFactor; passent = ent->s.number; for (i = 0; i < 10; i++) { VectorMA( muzzle, LIGHTNING_RANGE, forward, end ); //Sago: I'm not sure this should recieve backward reconciliation. It is not a real instant hit weapon, it can normally be dogded //unlagged - backward reconciliation #2 // backward-reconcile the other clients G_DoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT ); //unlagged - backward reconciliation #2 // put them back G_UndoTimeShiftFor( ent ); //unlagged - backward reconciliation #2 // if not the first trace (the lightning bounced of an invulnerability sphere) if (i) { // add bounced off lightning bolt temp entity // the first lightning bolt is a cgame only visual // tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT ); VectorCopy( tr.endpos, end ); SnapVector( end ); VectorCopy( end, tent->s.origin2 ); } if ( tr.entityNum == ENTITYNUM_NONE ) { return; } traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage) { if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, muzzle ); VectorSubtract( end, impactpoint, forward ); VectorNormalize(forward); // the player can hit him/herself with the bounced lightning passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, muzzle ); passent = traceEnt->s.number; } continue; } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } } if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; ent->client->accuracy[WP_LIGHTNING][1]++; } } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); } break; } } /* ====================================================================== NAILGUN ====================================================================== */ void Weapon_Nailgun_Fire (gentity_t *ent) { gentity_t *m; int count; for( count = 0; count < NUM_NAILSHOTS; count++ ) { m = fire_nail (ent, muzzle, forward, right, up ); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; } // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } /* ====================================================================== PROXIMITY MINE LAUNCHER ====================================================================== */ void weapon_proxlauncher_fire (gentity_t *ent) { gentity_t *m; // extra vertical velocity forward[2] += 0.2f; VectorNormalize( forward ); m = fire_prox (ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics } //====================================================================== /* =============== LogAccuracyHit =============== */ qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) { if( !target->takedamage ) { return qfalse; } if ( target == attacker ) { return qfalse; } if( !target->client ) { return qfalse; } if( !attacker->client ) { return qfalse; } if( target->client->ps.stats[STAT_HEALTH] <= 0 ) { return qfalse; } if ( OnSameTeam( target, attacker ) ) { return qfalse; } return qtrue; } /* =============== CalcMuzzlePoint set muzzle location relative to pivoting eye =============== */ void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { VectorCopy( ent->s.pos.trBase, muzzlePoint ); muzzlePoint[2] += ent->client->ps.viewheight; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // snap to integer coordinates for more efficient network bandwidth usage SnapVector( muzzlePoint ); } /* =============== CalcMuzzlePointOrigin set muzzle location relative to pivoting eye =============== */ void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { VectorCopy( ent->s.pos.trBase, muzzlePoint ); muzzlePoint[2] += ent->client->ps.viewheight; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // snap to integer coordinates for more efficient network bandwidth usage SnapVector( muzzlePoint ); } /* =============== FireWeapon =============== */ void FireWeapon( gentity_t *ent ) { //Make people drop out of follow mode (this should be moved, so people can change betwean players.) if (ent->client->sess.spectatorState == SPECTATOR_FOLLOW) { StopFollowing( ent ); return; } if (ent->client->ps.powerups[PW_QUAD] ) { s_quadFactor = g_quadfactor.value; } else { s_quadFactor = 1; } if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) { s_quadFactor *= 2; } if (ent->client->spawnprotected) ent->client->spawnprotected = qfalse; // track shots taken for accuracy tracking. Grapple is not a weapon and gauntet is just not tracked if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) { if( ent->s.weapon == WP_NAILGUN ) { ent->client->accuracy_shots += NUM_NAILSHOTS; ent->client->accuracy[WP_NAILGUN][0]++; } else { ent->client->accuracy_shots++; ent->client->accuracy[ent->s.weapon][0]++; } } // set aiming directions AngleVectors (ent->client->ps.viewangles, forward, right, up); CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle ); // fire the specific weapon switch( ent->s.weapon ) { case WP_GAUNTLET: Weapon_Gauntlet( ent ); break; case WP_LIGHTNING: Weapon_LightningFire( ent ); break; case WP_SHOTGUN: weapon_supershotgun_fire( ent ); break; case WP_MACHINEGUN: if ( g_gametype.integer != GT_TEAM ) { Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE ); } else { Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE ); } break; case WP_GRENADE_LAUNCHER: weapon_grenadelauncher_fire( ent ); break; case WP_ROCKET_LAUNCHER: Weapon_RocketLauncher_Fire( ent ); break; case WP_PLASMAGUN: Weapon_Plasmagun_Fire( ent ); break; case WP_RAILGUN: weapon_railgun_fire( ent ); break; case WP_BFG: BFG_Fire( ent ); break; case WP_GRAPPLING_HOOK: Weapon_GrapplingHook_Fire( ent ); break; case WP_NAILGUN: Weapon_Nailgun_Fire( ent ); break; case WP_PROX_LAUNCHER: weapon_proxlauncher_fire( ent ); break; case WP_CHAINGUN: Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE ); break; default: // FIXME G_Error( "Bad ent->s.weapon" ); break; } } /* =============== KamikazeRadiusDamage =============== */ static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) { float dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; if ( radius < 1 ) { radius = 1; } for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if (!ent->takedamage) { continue; } // dont hit things we have already hit if( ent->kamikazeTime > level.time ) { continue; } // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } // if( CanDamage (ent, origin) ) { VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE ); ent->kamikazeTime = level.time + 3000; // } } } /* =============== KamikazeShockWave =============== */ static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) { float dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; if ( radius < 1 ) radius = 1; for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; // dont hit things we have already hit if( ent->kamikazeShockTime > level.time ) { continue; } // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } // if( CanDamage (ent, origin) ) { VectorSubtract (ent->r.currentOrigin, origin, dir); dir[2] += 24; G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE ); // dir[2] = 0; VectorNormalize(dir); if ( ent->client ) { ent->client->ps.velocity[0] = dir[0] * push; ent->client->ps.velocity[1] = dir[1] * push; ent->client->ps.velocity[2] = 100; } ent->kamikazeShockTime = level.time + 3000; // } } } /* =============== KamikazeDamage =============== */ static void KamikazeDamage( gentity_t *self ) { int i; float t; gentity_t *ent; vec3_t newangles; self->count += 100; if (self->count >= KAMI_SHOCKWAVE_STARTTIME) { // shockwave push back t = self->count - KAMI_SHOCKWAVE_STARTTIME; KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400, (int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) ); } // if (self->count >= KAMI_EXPLODE_STARTTIME) { // do our damage t = self->count - KAMI_EXPLODE_STARTTIME; KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400, (int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) ); } // either cycle or kill self if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) { G_FreeEntity( self ); return; } self->nextthink = level.time + 100; // add earth quake effect newangles[0] = crandom() * 2; newangles[1] = crandom() * 2; newangles[2] = 0; for (i = 0; i < MAX_CLIENTS; i++) { ent = &g_entities[i]; if (!ent->inuse) continue; if (!ent->client) continue; if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) { ent->client->ps.velocity[0] += crandom() * 120; ent->client->ps.velocity[1] += crandom() * 120; ent->client->ps.velocity[2] = 30 + random() * 25; } ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]); ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]); ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]); } VectorCopy(newangles, self->movedir); } /* =============== G_StartKamikaze =============== */ void G_StartKamikaze( gentity_t *ent ) { gentity_t *explosion; gentity_t *te; vec3_t snapped; // start up the explosion logic explosion = G_Spawn(); explosion->s.eType = ET_EVENTS + EV_KAMIKAZE; explosion->eventTime = level.time; if ( ent->client ) { VectorCopy( ent->s.pos.trBase, snapped ); } else { VectorCopy( ent->activator->s.pos.trBase, snapped ); } SnapVector( snapped ); // save network bandwidth G_SetOrigin( explosion, snapped ); explosion->classname = "kamikaze"; explosion->s.pos.trType = TR_STATIONARY; explosion->kamikazeTime = level.time; explosion->think = KamikazeDamage; explosion->nextthink = level.time + 100; explosion->count = 0; VectorClear(explosion->movedir); trap_LinkEntity( explosion ); if (ent->client) { // explosion->activator = ent; // ent->s.eFlags &= ~EF_KAMIKAZE; // nuke the guy that used it G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE ); } else { if ( !strcmp(ent->activator->classname, "bodyque") ) { explosion->activator = &g_entities[ent->activator->r.ownerNum]; } else { explosion->activator = ent->activator; } } // play global sound at all clients te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND ); te->r.svFlags |= SVF_BROADCAST; te->s.eventParm = GTS_KAMIKAZE; } openarena_0.8.8.orig/code/game/syn.h0000644000175000017500000000250011656310264016030 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #define CONTEXT_ALL 0xFFFFFFFF #define CONTEXT_NORMAL 1 #define CONTEXT_NEARBYITEM 2 #define CONTEXT_CTFREDTEAM 4 #define CONTEXT_CTFBLUETEAM 8 #define CONTEXT_REPLY 16 #define CONTEXT_OBELISKREDTEAM 32 #define CONTEXT_OBELISKBLUETEAM 64 #define CONTEXT_HARVESTERREDTEAM 128 #define CONTEXT_HARVESTERBLUETEAM 256 #define CONTEXT_NAMES 1024 openarena_0.8.8.orig/code/game/g_bot.c0000644000175000017500000005531111656310264016314 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_bot.c #include "g_local.h" static int g_numBots; static char *g_botInfos[MAX_BOTS]; int g_numArenas; static char *g_arenaInfos[MAX_ARENAS]; #define BOT_BEGIN_DELAY_BASE 2000 #define BOT_BEGIN_DELAY_INCREMENT 1500 #define BOT_SPAWN_QUEUE_DEPTH 16 typedef struct { int clientNum; int spawnTime; } botSpawnQueue_t; //static int botBeginDelay = 0; // bk001206 - unused, init static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH]; vmCvar_t bot_minplayers; extern gentity_t *podium1; extern gentity_t *podium2; extern gentity_t *podium3; float trap_Cvar_VariableValue( const char *var_name ) { char buf[128]; trap_Cvar_VariableStringBuffer(var_name, buf, sizeof(buf)); return atof(buf); } /* =============== G_ParseInfos =============== */ int G_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "" ); } Info_SetValueForKey( info, key, token ); } if(!BG_CanAlloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1)) break; //Not enough memory. Don't even try //NOTE: extra space for arena number //KK-OAX Changed to Tremulous's BG_Alloc infos[count] = BG_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); if (infos[count]) { strcpy(infos[count], info); count++; } } return count; } /* =============== G_LoadArenasFromFile =============== */ static void G_LoadArenasFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_ARENAS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_ARENAS_TEXT ) { trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_ARENAS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] ); } /* =============== G_LoadArenas =============== */ static void G_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; g_numArenas = 0; trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { G_LoadArenasFromFile(arenasFile.string); } else { G_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); G_LoadArenasFromFile(filename); } trap_Printf( va( "%i arenas parsed\n", g_numArenas ) ); for( n = 0; n < g_numArenas; n++ ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } } /* =============== G_GetArenaInfoByNumber =============== */ const char *G_GetArenaInfoByMap( const char *map ) { int n; for( n = 0; n < g_numArenas; n++ ) { if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) { return g_arenaInfos[n]; } } return NULL; } /* ================= PlayerIntroSound ================= */ static void PlayerIntroSound( const char *modelAndSkin ) { char model[MAX_QPATH]; char *skin; Q_strncpyz( model, modelAndSkin, sizeof(model) ); skin = strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = model; } if( Q_stricmp( skin, "default" ) == 0 ) { skin = model; } trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) ); } /* =============== G_AddRandomBot =============== */ void G_AddRandomBot( int team ) { int i, n, num; float skill; char *value, netname[36], *teamstr; gclient_t *cl; if (!trap_AAS_Initialized()) return; //If no AAS then don't even try num = 0; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num++; } } num = random() * num; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num--; if (num <= 0) { skill = trap_Cvar_VariableValue( "g_spSkill" ); if (team == TEAM_RED) teamstr = "red"; else if (team == TEAM_BLUE) teamstr = "blue"; else teamstr = ""; strncpy(netname, value, sizeof(netname)-1); netname[sizeof(netname)-1] = '\0'; Q_CleanStr(netname); trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); return; } } } } /* =============== G_RemoveRandomBot =============== */ int G_RemoveRandomBot( int team ) { int i; gclient_t *cl; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } trap_SendConsoleCommand( EXEC_INSERT, va("clientkick %d\n", cl->ps.clientNum) ); return qtrue; } return qfalse; } /* =============== G_CountHumanPlayers =============== */ int G_CountHumanPlayers( int team ) { int i, num; gclient_t *cl; num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } num++; } return num; } /* =============== G_CountBotPlayers =============== */ int G_CountBotPlayers( int team ) { int i, n, num; gclient_t *cl; num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } num++; } for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { continue; } if ( botSpawnQueue[n].spawnTime > level.time ) { continue; } num++; } return num; } /* =============== G_CheckMinimumPlayers =============== */ void G_CheckMinimumPlayers( void ) { int minplayers; int humanplayers, botplayers; static int checkminimumplayers_time; if (level.intermissiontime) return; //only check once each 10 seconds if (checkminimumplayers_time > level.time - 10000) { return; } checkminimumplayers_time = level.time; trap_Cvar_Update(&bot_minplayers); minplayers = bot_minplayers.integer; if (minplayers <= 0) return; if (!trap_AAS_Initialized()) { minplayers = 0; checkminimumplayers_time = level.time+600*1000; return; //If no AAS then don't even try } if (g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { if (minplayers >= g_maxclients.integer / 2) { minplayers = (g_maxclients.integer / 2) -1; } humanplayers = G_CountHumanPlayers( TEAM_RED ); botplayers = G_CountBotPlayers( TEAM_RED ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_RED ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_RED ); } // humanplayers = G_CountHumanPlayers( TEAM_BLUE ); botplayers = G_CountBotPlayers( TEAM_BLUE ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_BLUE ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_BLUE ); } } else if (g_gametype.integer == GT_TOURNAMENT ) { if (minplayers >= g_maxclients.integer) { minplayers = g_maxclients.integer-1; } humanplayers = G_CountHumanPlayers( -1 ); botplayers = G_CountBotPlayers( -1 ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_FREE ); } else if (humanplayers + botplayers > minplayers && botplayers) { // try to remove spectators first if (!G_RemoveRandomBot( TEAM_SPECTATOR )) { // just remove the bot that is playing G_RemoveRandomBot( -1 ); } } } else if (g_gametype.integer == GT_FFA || g_gametype.integer == GT_LMS) { if (minplayers >= g_maxclients.integer) { minplayers = g_maxclients.integer-1; } humanplayers = G_CountHumanPlayers( TEAM_FREE ); botplayers = G_CountBotPlayers( TEAM_FREE ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_FREE ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_FREE ); } } } /* =============== G_CheckBotSpawn =============== */ void G_CheckBotSpawn( void ) { int n; char userinfo[MAX_INFO_VALUE]; G_CheckMinimumPlayers(); for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { continue; } if ( botSpawnQueue[n].spawnTime > level.time ) { continue; } ClientBegin( botSpawnQueue[n].clientNum ); botSpawnQueue[n].spawnTime = 0; if( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_GetUserinfo( botSpawnQueue[n].clientNum, userinfo, sizeof(userinfo) ); PlayerIntroSound( Info_ValueForKey (userinfo, "model") ); } } } /* =============== AddBotToSpawnQueue =============== */ static void AddBotToSpawnQueue( int clientNum, int delay ) { int n; for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { botSpawnQueue[n].spawnTime = level.time + delay; botSpawnQueue[n].clientNum = clientNum; return; } } G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" ); ClientBegin( clientNum ); } /* =============== G_RemoveQueuedBotBegin Called on client disconnect to make sure the delayed spawn doesn't happen on a freed index =============== */ void G_RemoveQueuedBotBegin( int clientNum ) { int n; for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( botSpawnQueue[n].clientNum == clientNum ) { botSpawnQueue[n].spawnTime = 0; return; } } } /* =============== G_BotConnect =============== */ qboolean G_BotConnect( int clientNum, qboolean restart ) { bot_settings_t settings; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); Q_strncpyz( settings.characterfile, Info_ValueForKey( userinfo, "characterfile" ), sizeof(settings.characterfile) ); settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) ); Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) ); if (!trap_AAS_Initialized() || !BotAISetupClient( clientNum, &settings, restart )) { trap_DropClient( clientNum, "BotAISetupClient failed" ); return qfalse; } return qtrue; } /* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "sarge/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); } /* =============== Svcmd_AddBot_f =============== */ void Svcmd_AddBot_f( void ) { float skill; int delay; char name[MAX_TOKEN_CHARS]; char altname[MAX_TOKEN_CHARS]; char string[MAX_TOKEN_CHARS]; char team[MAX_TOKEN_CHARS]; // are bots enabled? if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) || !trap_AAS_Initialized() ) { return; } // name trap_Argv( 1, name, sizeof( name ) ); if ( !name[0] ) { trap_Printf( "Usage: Addbot [skill 1-5] [team] [msec delay] [altname]\n" ); return; } // skill trap_Argv( 2, string, sizeof( string ) ); if ( !string[0] ) { skill = 4; } else { skill = atof( string ); } // team trap_Argv( 3, team, sizeof( team ) ); // delay trap_Argv( 4, string, sizeof( string ) ); if ( !string[0] ) { delay = 0; } else { delay = atoi( string ); } // alternative name trap_Argv( 5, altname, sizeof( altname ) ); G_AddBot( name, skill, team, delay, altname ); // if this was issued during gameplay and we are playing locally, // go ahead and load the bot's media immediately if ( level.time - level.startTime > 1000 && trap_Cvar_VariableIntegerValue( "cl_running" ) ) { trap_SendServerCommand( -1, "loaddefered\n" ); // FIXME: spelled wrong, but not changing for demo } } /* =============== Svcmd_BotList_f =============== */ void Svcmd_BotList_f( void ) { int i; char name[MAX_TOKEN_CHARS]; char funname[MAX_TOKEN_CHARS]; char model[MAX_TOKEN_CHARS]; char aifile[MAX_TOKEN_CHARS]; trap_Printf("^1name model aifile funname\n"); for (i = 0; i < g_numBots; i++) { strcpy(name, Info_ValueForKey( g_botInfos[i], "name" )); if ( !*name ) { strcpy(name, "UnnamedPlayer"); } strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" )); if ( !*funname ) { strcpy(funname, ""); } strcpy(model, Info_ValueForKey( g_botInfos[i], "model" )); if ( !*model ) { strcpy(model, "sarge/default"); } strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile")); if (!*aifile ) { strcpy(aifile, "bots/default_c.c"); } trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname)); } } /* =============== G_SpawnBots =============== */ static void G_SpawnBots( char *botList, int baseDelay ) { char *bot; char *p; float skill; int delay; char bots[MAX_INFO_VALUE]; podium1 = NULL; podium2 = NULL; podium3 = NULL; skill = trap_Cvar_VariableValue( "g_spSkill" ); if( skill < 1 ) { trap_Cvar_Set( "g_spSkill", "1" ); skill = 1; } else if ( skill > 5 ) { trap_Cvar_Set( "g_spSkill", "5" ); skill = 5; } Q_strncpyz( bots, botList, sizeof(bots) ); p = &bots[0]; delay = baseDelay; while( *p ) { //skip spaces while( *p && *p == ' ' ) { p++; } if( !p ) { break; } // mark start of bot name bot = p; // skip until space of null while( *p && *p != ' ' ) { p++; } if( *p ) { *p++ = 0; } // we must add the bot this way, calling G_AddBot directly at this stage // does "Bad Things" trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f free %i\n", bot, skill, delay) ); delay += BOT_BEGIN_DELAY_INCREMENT; } } /* =============== G_LoadBotsFromFile =============== */ static void G_LoadBotsFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_BOTS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_BOTS_TEXT ) { trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_BOTS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] ); } /* =============== G_LoadBots =============== */ static void G_LoadBots( void ) { vmCvar_t botsFile; int numdirs; char filename[128]; char dirlist[1024]; char* dirptr; int i; int dirlen; if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { return; } g_numBots = 0; trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); if( *botsFile.string ) { G_LoadBotsFromFile(botsFile.string); } else { G_LoadBotsFromFile("scripts/bots.txt"); } // get all bots from .bot files numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); G_LoadBotsFromFile(filename); } trap_Printf( va( "%i bots parsed\n", g_numBots ) ); } /* =============== G_GetBotInfoByNumber =============== */ char *G_GetBotInfoByNumber( int num ) { if( num < 0 || num >= g_numBots ) { trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) ); return NULL; } return g_botInfos[num]; } /* =============== G_GetBotInfoByName =============== */ char *G_GetBotInfoByName( const char *name ) { int n; char *value; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); if ( !Q_stricmp( value, name ) ) { return g_botInfos[n]; } } return NULL; } /* =============== G_InitBots =============== */ void G_InitBots( qboolean restart ) { int fragLimit; int timeLimit; const char *arenainfo; char *strValue; int basedelay; char map[MAX_QPATH]; char serverinfo[MAX_INFO_STRING]; G_LoadBots(); G_LoadArenas(); trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO ); if( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_GetServerinfo( serverinfo, sizeof(serverinfo) ); Q_strncpyz( map, Info_ValueForKey( serverinfo, "mapname" ), sizeof(map) ); arenainfo = G_GetArenaInfoByMap( map ); if ( !arenainfo ) { return; } strValue = Info_ValueForKey( arenainfo, "fraglimit" ); fragLimit = atoi( strValue ); if ( fragLimit ) { trap_Cvar_Set( "fraglimit", strValue ); } else { trap_Cvar_Set( "fraglimit", "0" ); } strValue = Info_ValueForKey( arenainfo, "timelimit" ); timeLimit = atoi( strValue ); if ( timeLimit ) { trap_Cvar_Set( "timelimit", strValue ); } else { trap_Cvar_Set( "timelimit", "0" ); } if ( !fragLimit && !timeLimit ) { trap_Cvar_Set( "fraglimit", "10" ); trap_Cvar_Set( "timelimit", "0" ); } basedelay = BOT_BEGIN_DELAY_BASE; strValue = Info_ValueForKey( arenainfo, "special" ); if( Q_stricmp( strValue, "training" ) == 0 ) { basedelay += 10000; } if( !restart ) { G_SpawnBots( Info_ValueForKey( arenainfo, "bots" ), basedelay ); } } } openarena_0.8.8.orig/code/game/ai_cmd.c0000644000175000017500000015063211656310264016440 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_cmd.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_cmd.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "../../ui/menudef.h" int notleader[MAX_CLIENTS]; #ifdef DEBUG /* ================== BotPrintTeamGoal ================== */ void BotPrintTeamGoal(bot_state_t *bs) { char netname[MAX_NETNAME]; float t; ClientName(bs->client, netname, sizeof(netname)); t = bs->teamgoal_time - FloatTime(); switch(bs->ltgtype) { case LTG_TEAMHELP: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t); break; } case LTG_TEAMACCOMPANY: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t); break; } case LTG_GETFLAG: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t); break; } case LTG_RUSHBASE: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t); break; } case LTG_RETURNFLAG: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t); break; } case LTG_ATTACKENEMYBASE: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna attack the enemy base for %1.0f secs\n", netname, t); break; } case LTG_HARVEST: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna harvest for %1.0f secs\n", netname, t); break; } case LTG_DEFENDKEYAREA: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t); break; } case LTG_GETITEM: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t); break; } case LTG_KILL: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t); break; } case LTG_PATROL: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t); break; } case LTG_POINTA: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna take care of point A for %1.0f secs\n", netname, t); break; } case LTG_POINTB: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna take care of point B for %1.0f secs\n", netname, t); break; } default: { if (bs->ctfroam_time > FloatTime()) { t = bs->ctfroam_time - FloatTime(); BotAI_Print(PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t); } else { BotAI_Print(PRT_MESSAGE, "%s: I've got a regular goal\n", netname); } } } } #endif //DEBUG /* ================== BotGetItemTeamGoal FIXME: add stuff like "upper rocket launcher" "the rl near the railgun", "lower grenade launcher" etc. ================== */ int BotGetItemTeamGoal(char *goalname, bot_goal_t *goal) { int i; if (!strlen(goalname)) return qfalse; i = -1; do { i = trap_BotGetLevelItemGoal(i, goalname, goal); if (i > 0) { //do NOT defend dropped items if (goal->flags & GFL_DROPPED) continue; return qtrue; } } while(i > 0); return qfalse; } /* ================== BotGetMessageTeamGoal ================== */ int BotGetMessageTeamGoal(bot_state_t *bs, char *goalname, bot_goal_t *goal) { bot_waypoint_t *cp; if (BotGetItemTeamGoal(goalname, goal)) return qtrue; cp = BotFindWayPoint(bs->checkpoints, goalname); if (cp) { memcpy(goal, &cp->goal, sizeof(bot_goal_t)); return qtrue; } return qfalse; } /* ================== BotGetTime ================== */ float BotGetTime(bot_match_t *match) { bot_match_t timematch; char timestring[MAX_MESSAGE_SIZE]; float t; //if the matched string has a time if (match->subtype & ST_TIME) { //get the time string trap_BotMatchVariable(match, TIME, timestring, MAX_MESSAGE_SIZE); //match it to find out if the time is in seconds or minutes if (trap_BotFindMatch(timestring, &timematch, MTCONTEXT_TIME)) { if (timematch.type == MSG_FOREVER) { t = 99999999.0f; } else if (timematch.type == MSG_FORAWHILE) { t = 10 * 60; // 10 minutes } else if (timematch.type == MSG_FORALONGTIME) { t = 30 * 60; // 30 minutes } else { trap_BotMatchVariable(&timematch, TIME, timestring, MAX_MESSAGE_SIZE); if (timematch.type == MSG_MINUTES) t = atof(timestring) * 60; else if (timematch.type == MSG_SECONDS) t = atof(timestring); else t = 0; } //if there's a valid time if (t > 0) return FloatTime() + t; } } return 0; } /* ================== FindClientByName ================== */ int FindClientByName(char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } return -1; } /* ================== FindEnemyByName ================== */ int FindEnemyByName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } return -1; } /* ================== NumPlayersOnSameTeam ================== */ int NumPlayersOnSameTeam(bot_state_t *bs) { int i, num; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i+1)) num++; } } return num; } /* ================== TeamPlayIsOn ================== */ int BotGetPatrolWaypoints(bot_state_t *bs, bot_match_t *match) { char keyarea[MAX_MESSAGE_SIZE]; int patrolflags; bot_waypoint_t *wp, *newwp, *newpatrolpoints; bot_match_t keyareamatch; bot_goal_t goal; newpatrolpoints = NULL; patrolflags = 0; // trap_BotMatchVariable(match, KEYAREA, keyarea, MAX_MESSAGE_SIZE); // while(1) { if (!trap_BotFindMatch(keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA)) { trap_EA_SayTeam(bs->client, "what do you say?"); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } trap_BotMatchVariable(&keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE); if (!BotGetMessageTeamGoal(bs, keyarea, &goal)) { //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL); //trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } //create a new waypoint newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum); if (!newwp) break; //add the waypoint to the patrol points newwp->next = NULL; for (wp = newpatrolpoints; wp && wp->next; wp = wp->next); if (!wp) { newpatrolpoints = newwp; newwp->prev = NULL; } else { wp->next = newwp; newwp->prev = wp; } // if (keyareamatch.subtype & ST_BACK) { patrolflags = PATROL_LOOP; break; } else if (keyareamatch.subtype & ST_REVERSE) { patrolflags = PATROL_REVERSE; break; } else if (keyareamatch.subtype & ST_MORE) { trap_BotMatchVariable(&keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE); } else { break; } } // if (!newpatrolpoints || !newpatrolpoints->next) { trap_EA_SayTeam(bs->client, "I need more key points to patrol\n"); BotFreeWaypoints(newpatrolpoints); newpatrolpoints = NULL; return qfalse; } // BotFreeWaypoints(bs->patrolpoints); bs->patrolpoints = newpatrolpoints; // bs->curpatrolpoint = bs->patrolpoints; bs->patrolflags = patrolflags; // return qtrue; } /* ================== BotAddressedToBot ================== */ int BotAddressedToBot(bot_state_t *bs, bot_match_t *match) { char addressedto[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char name[MAX_MESSAGE_SIZE]; char botname[128]; int client; bot_match_t addresseematch; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientOnSameTeamFromName(bs, netname); if (client < 0) return qfalse; //if the message is addressed to someone if (match->subtype & ST_ADDRESSED) { trap_BotMatchVariable(match, ADDRESSEE, addressedto, sizeof(addressedto)); //the name of this bot ClientName(bs->client, botname, 128); // while(trap_BotFindMatch(addressedto, &addresseematch, MTCONTEXT_ADDRESSEE)) { if (addresseematch.type == MSG_EVERYONE) { return qtrue; } else if (addresseematch.type == MSG_MULTIPLENAMES) { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, sizeof(name)); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } trap_BotMatchVariable(&addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE); } else { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } break; } } //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto); //trap_EA_Say(bs->client, buf); return qfalse; } else { bot_match_t tellmatch; tellmatch.type = 0; //if this message wasn't directed solely to this bot if (!trap_BotFindMatch(match->string, &tellmatch, MTCONTEXT_REPLYCHAT) || tellmatch.type != MSG_CHATTELL) { //make sure not everyone reacts to this message if (random() > (float ) 1.0 / (NumPlayersOnSameTeam(bs)-1)) return qfalse; } } return qtrue; } /* ================== BotGPSToPosition ================== */ int BotGPSToPosition(char *buf, vec3_t position) { int i, j = 0; int num, sign; for (i = 0; i < 3; i++) { num = 0; while(buf[j] == ' ') j++; if (buf[j] == '-') { j++; sign = -1; } else { sign = 1; } while (buf[j]) { if (buf[j] >= '0' && buf[j] <= '9') { num = num * 10 + buf[j] - '0'; j++; } else { j++; break; } } BotAI_Print(PRT_MESSAGE, "%d\n", sign * num); position[i] = (float) sign * num; } return qtrue; } /* ================== BotMatch_HelpAccompany ================== */ void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) { int client, other, areanum; char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; bot_match_t teammatematch; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); //get the client to help if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && //if someone asks for him or herself teammatematch.type == MSG_ME) { //get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } else { //asked for someone else client = FindClientByName(teammate); //if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { //FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } //if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { if (other) BotAI_BotInitialChat(bs, "whois", teammate, NULL); else BotAI_BotInitialChat(bs, "whois", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } //don't help or accompany yourself if (client == bs->client) { return; } // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) {// && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //if no teamgoal yet if (bs->teamgoal.entitynum < 0) { //if near an item if (match->subtype & ST_NEARITEM) { //get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } } } // if (bs->teamgoal.entitynum < 0) { if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TEAM); return; } //the team mate bs->teammate = client; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //last time the team mate was assumed visible bs->teammatevisible_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the ltg type if (match->type == MSG_HELP) { bs->ltgtype = LTG_TEAMHELP; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_HELP_TIME; } else { bs->ltgtype = LTG_TEAMACCOMPANY; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->formation_dist = 3.5 * 32; //3.5 meter bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_DefendKeyArea ================== */ void BotMatch_DefendKeyArea(bot_state_t *bs, bot_match_t *match) { char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_TakeA For Double Domination ================== */ void BotMatch_TakeA(bot_state_t *bs, bot_match_t *match) { // char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable /*trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; }*/ // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_POINTA; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + DD_POINTA; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_TakeB For Double Domination ================== */ void BotMatch_TakeB(bot_state_t *bs, bot_match_t *match) { // char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable /*trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); //return; }*/ // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_POINTB; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + DD_POINTA; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_GetItem ================== */ void BotMatch_GetItem(bot_state_t *bs, bot_match_t *match) { char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientOnSameTeamFromName(bs, netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETITEM; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_GETITEM_TIME; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_Camp ================== */ void BotMatch_Camp(bot_state_t *bs, bot_match_t *match) { int client, areanum; char netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); //asked for someone else client = FindClientByName(netname); //if there's no valid client with this name if (client < 0) { BotAI_BotInitialChat(bs, "whois", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } //get the match variable trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); //in CTF it could be the base if (match->subtype & ST_THERE) { //camp at the spot the bot is currently standing bs->teamgoal.entitynum = bs->entitynum; bs->teamgoal.areanum = bs->areanum; VectorCopy(bs->origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } else if (match->subtype & ST_HERE) { //if this is the bot self if (client == bs->client) return; // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) {// && trap_AAS_AreaReachability(areanum)) { //NOTE: just assume the bot knows where the person is //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); //} } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } } else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //client = ClientFromName(netname); //trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_CAMPORDER; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME; //not arrived yet bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_Patrol ================== */ void BotMatch_Patrol(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the patrol waypoints if (!BotGetPatrolWaypoints(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_PATROL; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if not set already if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_PATROL_TIME; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_GetFlag ================== */ void BotMatch_GetFlag(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else if (gametype == GT_1FCTF) { if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; // get an alternate route in ctf if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_AttackEnemyBase ================== */ void BotMatch_AttackEnemyBase(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF|| gametype == GT_CTF_ELIMINATION) { BotMatch_GetFlag(bs, match); } else if (gametype == GT_1FCTF || gametype == GT_OBELISK || gametype == GT_HARVESTER) { if (!redobelisk.areanum || !blueobelisk.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; bs->attackaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_Harvest ================== */ void BotMatch_Harvest(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_HARVESTER) { if (!neutralobelisk.areanum || !redobelisk.areanum || !blueobelisk.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_HARVEST; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; bs->harvestaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_RushBase ================== */ void BotMatch_RushBase(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF|| gametype == GT_CTF_ELIMINATION) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else if (gametype == GT_1FCTF || gametype == GT_HARVESTER) { if (!redobelisk.areanum || !blueobelisk.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RUSHBASE; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_TaskPreference ================== */ void BotMatch_TaskPreference(bot_state_t *bs, bot_match_t *match) { char netname[MAX_NETNAME]; char teammatename[MAX_MESSAGE_SIZE]; int teammate, preference; ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; trap_BotMatchVariable(match, NETNAME, teammatename, sizeof(teammatename)); teammate = ClientFromName(teammatename); if (teammate < 0) return; preference = BotGetTeamMateTaskPreference(bs, teammate); switch(match->subtype) { case ST_DEFENDER: { preference &= ~TEAMTP_ATTACKER; preference |= TEAMTP_DEFENDER; break; } case ST_ATTACKER: { preference &= ~TEAMTP_DEFENDER; preference |= TEAMTP_ATTACKER; break; } case ST_ROAMER: { preference &= ~(TEAMTP_ATTACKER|TEAMTP_DEFENDER); break; } } BotSetTeamMateTaskPreference(bs, teammate, preference); // EasyClientName(teammate, teammatename, sizeof(teammatename)); BotAI_BotInitialChat(bs, "keepinmind", teammatename, NULL); trap_BotEnterChat(bs->cs, teammate, CHAT_TELL); BotVoiceChatOnly(bs, teammate, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotMatch_ReturnFlag ================== */ void BotMatch_ReturnFlag(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; //if not in CTF mode if (gametype != GT_CTF && gametype != GT_CTF_ELIMINATION && gametype != GT_1FCTF) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RETURNFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; bs->rushbaseaway_time = 0; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_JoinSubteam ================== */ void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) { char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the sub team name trap_BotMatchVariable(match, TEAMNAME, teammate, sizeof(teammate)); //set the sub team name strncpy(bs->subteam, teammate, 32); bs->subteam[31] = '\0'; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_LeaveSubteam ================== */ void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // if (strlen(bs->subteam)) { BotAI_BotInitialChat(bs, "leftteam", bs->subteam, NULL); trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } //end if strcpy(bs->subteam, ""); } /* ================== BotMatch_LeaveSubteam ================== */ void BotMatch_WhichTeam(bot_state_t *bs, bot_match_t *match) { if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // if (strlen(bs->subteam)) { BotAI_BotInitialChat(bs, "inteam", bs->subteam, NULL); } else { BotAI_BotInitialChat(bs, "noteam", NULL); } trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); } /* ================== BotMatch_CheckPoint ================== */ void BotMatch_CheckPoint(bot_state_t *bs, bot_match_t *match) { int areanum, client; char buf[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; vec3_t position; bot_waypoint_t *cp; if (!TeamPlayIsOn()) return; // trap_BotMatchVariable(match, POSITION, buf, MAX_MESSAGE_SIZE); VectorClear(position); // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); //BotGPSToPosition(buf, position); sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]); position[2] += 0.5; areanum = BotPointAreaNum(position); if (!areanum) { if (BotAddressedToBot(bs, match)) { BotAI_BotInitialChat(bs, "checkpoint_invalid", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } return; } // trap_BotMatchVariable(match, NAME, buf, MAX_MESSAGE_SIZE); //check if there already exists a checkpoint with this name cp = BotFindWayPoint(bs->checkpoints, buf); if (cp) { if (cp->next) cp->next->prev = cp->prev; if (cp->prev) cp->prev->next = cp->next; else bs->checkpoints = cp->next; cp->inuse = qfalse; } //create a new check point cp = BotCreateWayPoint(buf, position, areanum); //add the check point to the bot's known chech points cp->next = bs->checkpoints; if (bs->checkpoints) bs->checkpoints->prev = cp; bs->checkpoints = cp; // if (BotAddressedToBot(bs, match)) { Com_sprintf(buf, sizeof(buf), "%1.0f %1.0f %1.0f", cp->goal.origin[0], cp->goal.origin[1], cp->goal.origin[2]); BotAI_BotInitialChat(bs, "checkpoint_confirm", cp->name, buf, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } } /* ================== BotMatch_FormationSpace ================== */ void BotMatch_FormationSpace(bot_state_t *bs, bot_match_t *match) { char buf[MAX_MESSAGE_SIZE]; float space; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NUMBER, buf, MAX_MESSAGE_SIZE); //if it's the distance in feet if (match->subtype & ST_FEET) space = 0.3048 * 32 * atof(buf); //else it's in meters else space = 32 * atof(buf); //check if the formation intervening space is valid if (space < 48 || space > 500) space = 100; bs->formation_dist = space; } /* ================== BotMatch_Dismiss ================== */ void BotMatch_Dismiss(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); // bs->decisionmaker = client; // bs->ltgtype = 0; bs->lead_time = 0; bs->lastgoal_ltgtype = 0; // BotAI_BotInitialChat(bs, "dismissed", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_Suicide ================== */ void BotMatch_Suicide(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_EA_Command(bs->client, "kill"); // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); // BotVoiceChat(bs, client, VOICECHAT_TAUNT); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotMatch_StartTeamLeaderShip ================== */ void BotMatch_StartTeamLeaderShip(bot_state_t *bs, bot_match_t *match) { int client; char teammate[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; //if chats for him or herself if (match->subtype & ST_I) { //get the team mate that will be the team leader trap_BotMatchVariable(match, NETNAME, teammate, sizeof(teammate)); strncpy(bs->teamleader, teammate, sizeof(bs->teamleader)); bs->teamleader[sizeof(bs->teamleader)-1] = '\0'; } //chats for someone else else { //get the team mate that will be the team leader trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); client = FindClientByName(teammate); if (client >= 0) ClientName(client, bs->teamleader, sizeof(bs->teamleader)); } } /* ================== BotMatch_StopTeamLeaderShip ================== */ void BotMatch_StopTeamLeaderShip(bot_state_t *bs, bot_match_t *match) { int client; char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; //get the team mate that stops being the team leader trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); //if chats for him or herself if (match->subtype & ST_I) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); } //chats for someone else else { client = FindClientByName(teammate); } //end else if (client >= 0) { if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) { bs->teamleader[0] = '\0'; notleader[client] = qtrue; } } } /* ================== BotMatch_WhoIsTeamLeader ================== */ void BotMatch_WhoIsTeamLeader(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; ClientName(bs->client, netname, sizeof(netname)); //if this bot IS the team leader if (!Q_stricmp(netname, bs->teamleader)) { trap_EA_SayTeam(bs->client, "I'm the team leader\n"); } } /* ================== BotMatch_WhatAreYouDoing ================== */ void BotMatch_WhatAreYouDoing(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; char goalname[MAX_MESSAGE_SIZE]; int client; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "helping", netname, NULL); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "accompanying", netname, NULL); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_BotInitialChat(bs, "defending", goalname, NULL); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_BotInitialChat(bs, "gettingitem", goalname, NULL); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "killing", netname, NULL); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_BotInitialChat(bs, "camping", NULL); break; } case LTG_PATROL: { BotAI_BotInitialChat(bs, "patrolling", NULL); break; } case LTG_GETFLAG: { BotAI_BotInitialChat(bs, "capturingflag", NULL); break; } case LTG_RUSHBASE: { BotAI_BotInitialChat(bs, "rushingbase", NULL); break; } case LTG_RETURNFLAG: { BotAI_BotInitialChat(bs, "returningflag", NULL); break; } case LTG_ATTACKENEMYBASE: { BotAI_BotInitialChat(bs, "attackingenemybase", NULL); break; } case LTG_HARVEST: { BotAI_BotInitialChat(bs, "harvesting", NULL); break; } //#endif case LTG_POINTA: { BotAI_BotInitialChat(bs, "dd_pointa", NULL); break; } case LTG_POINTB: { BotAI_BotInitialChat(bs, "dd_pointb", NULL); break; } default: { BotAI_BotInitialChat(bs, "roaming", NULL); break; } } //chat what the bot is doing trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_WhatIsMyCommand ================== */ void BotMatch_WhatIsMyCommand(bot_state_t *bs, bot_match_t *match) { char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; bs->forceorders = qtrue; } /* ================== BotNearestVisibleItem ================== */ float BotNearestVisibleItem(bot_state_t *bs, char *itemname, bot_goal_t *goal) { int i; char name[64]; bot_goal_t tmpgoal; float dist, bestdist; vec3_t dir; bsp_trace_t trace; bestdist = 999999; i = -1; do { i = trap_BotGetLevelItemGoal(i, itemname, &tmpgoal); trap_BotGoalName(tmpgoal.number, name, sizeof(name)); if (Q_stricmp(itemname, name) != 0) continue; VectorSubtract(tmpgoal.origin, bs->origin, dir); dist = VectorLength(dir); if (dist < bestdist) { //trace from start to end BotAI_Trace(&trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (trace.fraction >= 1.0) { bestdist = dist; memcpy(goal, &tmpgoal, sizeof(bot_goal_t)); } } } while(i > 0); return bestdist; } /* ================== BotMatch_WhereAreYou ================== */ void BotMatch_WhereAreYou(bot_state_t *bs, bot_match_t *match) { float dist, bestdist; int i, bestitem, redtt, bluett, client; bot_goal_t goal; char netname[MAX_MESSAGE_SIZE]; char *nearbyitems[] = { "Shotgun", "Grenade Launcher", "Rocket Launcher", "Plasmagun", "Railgun", "Lightning Gun", "BFG10K", "Quad Damage", "Regeneration", "Battle Suit", "Speed", "Invisibility", "Flight", "Armor", "Heavy Armor", "Red Flag", "Blue Flag", "Nailgun", "Prox Launcher", "Chaingun", "Scout", "Guard", "Doubler", "Ammo Regen", "Neutral Flag", "Red Obelisk", "Blue Obelisk", "Neutral Obelisk", NULL }; // if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; bestitem = -1; bestdist = 999999; for (i = 0; nearbyitems[i]; i++) { dist = BotNearestVisibleItem(bs, nearbyitems[i], &goal); if (dist < bestdist) { bestdist = dist; bestitem = i; } } if (bestitem != -1) { if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION || gametype == GT_1FCTF ) { redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT); bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT); if (redtt < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL); } else if (bluett < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL); } else { BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); } } else if (gametype == GT_OBELISK || gametype == GT_HARVESTER) { redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, redobelisk.areanum, TFL_DEFAULT); bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, blueobelisk.areanum, TFL_DEFAULT); if (redtt < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL); } else if (bluett < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL); } else { BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); } } else { BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); } trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } } /* ================== BotMatch_LeadTheWay ================== */ void BotMatch_LeadTheWay(bot_state_t *bs, bot_match_t *match) { aas_entityinfo_t entinfo; char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE]; int client, areanum, other; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //if someone asks for someone else if (match->subtype & ST_SOMEONE) { //get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); client = FindClientByName(teammate); //if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { //FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } else { //get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } //if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { BotAI_BotInitialChat(bs, "whois", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // bs->lead_teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { bs->lead_teamgoal.entitynum = client; bs->lead_teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->lead_teamgoal.origin); VectorSet(bs->lead_teamgoal.mins, -8, -8, -8); VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8); } } if (bs->teamgoal.entitynum < 0) { if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } bs->lead_teammate = client; bs->lead_time = FloatTime() + TEAM_LEAD_TIME; bs->leadvisible_time = 0; bs->leadmessage_time = -(FloatTime() + 2 * random()); } /* ================== BotMatch_Kill ================== */ void BotMatch_Kill(bot_state_t *bs, bot_match_t *match) { char enemy[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; trap_BotMatchVariable(match, ENEMY, enemy, sizeof(enemy)); // client = FindEnemyByName(bs, enemy); if (client < 0) { BotAI_BotInitialChat(bs, "whois", enemy, NULL); trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } bs->teamgoal.entitynum = client; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_KILL; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_KILL_SOMEONE; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_CTF ================== */ void BotMatch_CTF(bot_state_t *bs, bot_match_t *match) { char flag[128], netname[MAX_NETNAME]; if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { trap_BotMatchVariable(match, FLAG, flag, sizeof(flag)); if (match->subtype & ST_GOTFLAG) { if (!Q_stricmp(flag, "red")) { bs->redflagstatus = 1; if (BotTeam(bs) == TEAM_BLUE) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); bs->flagcarrier = ClientFromName(netname); } } else { bs->blueflagstatus = 1; if (BotTeam(bs) == TEAM_RED) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); bs->flagcarrier = ClientFromName(netname); } } bs->flagstatuschanged = 1; bs->lastflagcapture_time = FloatTime(); } else if (match->subtype & ST_CAPTUREDFLAG) { bs->redflagstatus = 0; bs->blueflagstatus = 0; bs->flagcarrier = 0; bs->flagstatuschanged = 1; } else if (match->subtype & ST_RETURNEDFLAG) { if (!Q_stricmp(flag, "red")) bs->redflagstatus = 0; else bs->blueflagstatus = 0; bs->flagstatuschanged = 1; } } else if (gametype == GT_1FCTF) { if (match->subtype & ST_1FCTFGOTFLAG) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); bs->flagcarrier = ClientFromName(netname); } } } void BotMatch_EnterGame(bot_state_t *bs, bot_match_t *match) { int client; char netname[MAX_NETNAME]; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); if (client >= 0) { notleader[client] = qfalse; } //NOTE: eliza chats will catch this //Com_sprintf(buf, sizeof(buf), "heya %s", netname); //EA_Say(bs->client, buf); } void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) { int client; char netname[MAX_NETNAME]; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); if (!BotSameTeam(bs, client)) return; Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader)); } /* ================== BotMatchMessage ================== */ int BotMatchMessage(bot_state_t *bs, char *message) { bot_match_t match; match.type = 0; //if it is an unknown message if (!trap_BotFindMatch(message, &match, MTCONTEXT_MISC |MTCONTEXT_INITIALTEAMCHAT |MTCONTEXT_CTF |MTCONTEXT_DD)) { return qfalse; } //react to the found message switch(match.type) { case MSG_HELP: //someone calling for help case MSG_ACCOMPANY: //someone calling for company { BotMatch_HelpAccompany(bs, &match); break; } case MSG_DEFENDKEYAREA: //teamplay defend a key area { BotMatch_DefendKeyArea(bs, &match); break; } case MSG_CAMP: //camp somewhere { BotMatch_Camp(bs, &match); break; } case MSG_PATROL: //patrol between several key areas { BotMatch_Patrol(bs, &match); break; } //CTF & 1FCTF case MSG_GETFLAG: //ctf get the enemy flag { BotMatch_GetFlag(bs, &match); break; } //CTF & 1FCTF & Obelisk & Harvester case MSG_ATTACKENEMYBASE: { BotMatch_AttackEnemyBase(bs, &match); break; } //Harvester case MSG_HARVEST: { BotMatch_Harvest(bs, &match); break; } //CTF & 1FCTF & Harvester case MSG_RUSHBASE: //ctf rush to the base { BotMatch_RushBase(bs, &match); break; } //CTF & 1FCTF case MSG_RETURNFLAG: { BotMatch_ReturnFlag(bs, &match); break; } //CTF & 1FCTF & Obelisk & Harvester case MSG_TASKPREFERENCE: { BotMatch_TaskPreference(bs, &match); break; } //CTF & 1FCTF case MSG_CTF: { BotMatch_CTF(bs, &match); break; } case MSG_GETITEM: { BotMatch_GetItem(bs, &match); break; } case MSG_JOINSUBTEAM: //join a sub team { BotMatch_JoinSubteam(bs, &match); break; } case MSG_LEAVESUBTEAM: //leave a sub team { BotMatch_LeaveSubteam(bs, &match); break; } case MSG_WHICHTEAM: { BotMatch_WhichTeam(bs, &match); break; } case MSG_CHECKPOINT: //remember a check point { BotMatch_CheckPoint(bs, &match); break; } case MSG_CREATENEWFORMATION: //start the creation of a new formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONPOSITION: //tell someone his/her position in the formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONSPACE: //set the formation space { BotMatch_FormationSpace(bs, &match); break; } case MSG_DOFORMATION: //form a certain formation { break; } case MSG_DISMISS: //dismiss someone { BotMatch_Dismiss(bs, &match); break; } case MSG_STARTTEAMLEADERSHIP: //someone will become the team leader { BotMatch_StartTeamLeaderShip(bs, &match); break; } case MSG_STOPTEAMLEADERSHIP: //someone will stop being the team leader { BotMatch_StopTeamLeaderShip(bs, &match); break; } case MSG_WHOISTEAMLAEDER: { BotMatch_WhoIsTeamLeader(bs, &match); break; } case MSG_WHATAREYOUDOING: //ask a bot what he/she is doing { BotMatch_WhatAreYouDoing(bs, &match); break; } case MSG_WHATISMYCOMMAND: { BotMatch_WhatIsMyCommand(bs, &match); break; } case MSG_WHEREAREYOU: { BotMatch_WhereAreYou(bs, &match); break; } case MSG_LEADTHEWAY: { BotMatch_LeadTheWay(bs, &match); break; } case MSG_KILL: { BotMatch_Kill(bs, &match); break; } case MSG_ENTERGAME: //someone entered the game { BotMatch_EnterGame(bs, &match); break; } case MSG_NEWLEADER: { BotMatch_NewLeader(bs, &match); break; } case MSG_WAIT: { break; } case MSG_SUICIDE: { BotMatch_Suicide(bs, &match); break; } case MSG_TAKEA: { BotMatch_TakeA(bs, &match); break; } case MSG_TAKEB: { BotMatch_TakeB(bs, &match); break; } default: { BotAI_Print(PRT_MESSAGE, "unknown match type\n"); break; } } return qtrue; } openarena_0.8.8.orig/code/game/g_mem.c0000644000175000017500000000324511656310264016305 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // g_mem.c // #include "g_local.h" #define POOLSIZE (256 * 1024) static char memoryPool[POOLSIZE]; static int allocPoint; void *G_Alloc( int size ) { char *p; if ( g_debugAlloc.integer ) { G_Printf( "G_Alloc of %i bytes (%i left)\n", size, POOLSIZE - allocPoint - ( ( size + 31 ) & ~31 ) ); } if ( allocPoint + size > POOLSIZE ) { G_Error( "G_Alloc: failed on allocation of %i bytes\n", size ); // bk010103 - was %u, but is signed return NULL; } p = &memoryPool[allocPoint]; allocPoint += ( size + 31 ) & ~31; return p; } void G_InitMemory( void ) { allocPoint = 0; } void Svcmd_GameMem_f( void ) { G_Printf( "Game memory status: %i out of %i bytes allocated\n", allocPoint, POOLSIZE ); } openarena_0.8.8.orig/code/game/challenges.xml0000644000175000017500000002252011656310264017701 0ustar smcvsmcv 0 GENERAL_ Overall statistics Totals 0 TEST 1 Test Used for test 1 TOTALKILLS Kills Total number of players killed 2 TOTALDEATHS Deaths Number of times killed by another player 3 TOTALGAMES Games Number of games played with 2 or more human players 1 GAMETYPE_ Gametype wins Number of wins in the different gametypes 1 FFA_WINS Deathmatch Number of victories in deathmatch games 2 TOURNEY_WINS Tournament Number of victories in tournament games 3 TDM_WINS Team Deathmatch Number of victories in team deathmatch games 4 CTF_WINS Capture the flag Number of victories in capture the flag games 5 1FCTF_WINS One flag capture Number of victories in one flag capture games 6 OVERLOAD_WINS Obelisk Number of victories in obelisk games 7 HARVESTER_WINS Harvester Number of victories in harvester games 8 ELIMINATION_WINS Elimination Number of victories in elimination games 9 CTF_ELIMINATION_WINS CTF Elimination Number of victories in CTF elimination games 10 LMS_WINS Last man standing Number of victories in last man standing games 11 DD_WINS Double Domination Number of victories in double domination games 12 DOM_WINS Domination Number of victories in domination games 2 WEAPON_ Weapon kills Number of kills with different weapons 1 GAUNTLET_KILLS Gauntlet Number of kills with gauntlet 2 MACHINEGUN_KILLS Machinegun Number of kills with machinegun 3 SHOTGUN_KILLS Shotgun Number of kills with shotgun 4 GRANADE_KILLS Granade launcher Number of kills with granade launcher 5 ROCKET_KILLS Rocket launcher Number of kills with rocket launcher 6 LIGHTNING_KILLS Lightning gun Number of kills with lightning 7 PLASMA_KILLS Plasma gun Number of kills with plasma 8 RAIL_KILLS Railgun Number of kills with railgun 9 BFG_KILLS BFG Number of kills with BFG 10 GRAPPLE_KILLS Grapple Number of kills with grapple gun (almost impossible) 11 CHAINGUN_KILLS Chaingun Number of kills with the chaingun 12 NAILGUN_KILLS Nailgun Number of kills with nails 13 MINE_KILLS Mines Number of kills with mines 14 PUSH_KILLS Push kills Number of kills by pushing players into lava or void 15 INSTANT_RAIL_KILLS Instant rail Number of kills with railgun in instantgib 16 TELEFRAG_KILLS Telefrags Number of kills by telefrag 17 CRUSH_KILLS Crushing Number of kills by crushing 3 AWARD_ Awards Number of times different awards have been archived 101 1 Gauntlet Number of kills by gauntlet 1 IMPRESSIVE Impressive Two hits in a row with railgun 2 EXCELLENT Excelent Two kills in two seconds 3 CAPTURE Capture Scoring in team games 4 ASSIST Assist Asisting a capture 5 DEFENCE Defence Defending objective 4 POWERUP_ Power-ups Number of kills with different powerups involved 1 QUAD_KILL Defence Defending objective 2 SPEED_KILL Defence Defending objective 3 FLIGHT_KILL Defence Defending objective 4 INVIS_KILL Defence Defending objective 5 MULTI_KILL Defence Defending objective 6 DEFENCE Defence Defending objective 7 DEFENCE Defence Defending objective 8 DEFENCE Defence Defending objective 9 DEFENCE Defence Defending objective 10 DEFENCE Defence Defending objective 11 DEFENCE Defence Defending objective 12 DEFENCE Defence Defending objective openarena_0.8.8.orig/code/game/ai_vcmd.c0000644000175000017500000003424411656310264016626 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_vcmd.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_vcmd.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" #include "ai_vcmd.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "../../ui/menudef.h" typedef struct voiceCommand_s { char *cmd; void (*func)(bot_state_t *bs, int client, int mode); } voiceCommand_t; /* ================== BotVoiceChat_GetFlag ================== */ void BotVoiceChat_GetFlag(bot_state_t *bs, int client, int mode) { // if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else if (gametype == GT_1FCTF) { if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; // get an alternate route in ctf if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Offense ================== */ void BotVoiceChat_Offense(bot_state_t *bs, int client, int mode) { if ( gametype == GT_CTF || gametype == GT_CTF_ELIMINATION || gametype == GT_1FCTF ) { BotVoiceChat_GetFlag(bs, client, mode); return; } if (gametype == GT_HARVESTER) { // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_HARVEST; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME; bs->harvestaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } else { // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; bs->attackaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Defend ================== */ void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode) { if ( gametype == GT_OBELISK || gametype == GT_HARVESTER) { // switch(BotTeam(bs)) { case TEAM_RED: memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); break; default: return; } } else if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION || gametype == GT_1FCTF ) { // switch(BotTeam(bs)) { case TEAM_RED: memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); break; default: return; } } else { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_DefendFlag ================== */ void BotVoiceChat_DefendFlag(bot_state_t *bs, int client, int mode) { BotVoiceChat_Defend(bs, client, mode); } /* ================== BotVoiceChat_Patrol ================== */ void BotVoiceChat_Patrol(bot_state_t *bs, int client, int mode) { // bs->decisionmaker = client; // bs->ltgtype = 0; bs->lead_time = 0; bs->lastgoal_ltgtype = 0; // BotAI_BotInitialChat(bs, "dismissed", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, -1, VOICECHAT_ONPATROL); // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Camp ================== */ void BotVoiceChat_Camp(bot_state_t *bs, int client, int mode) { int areanum; aas_entityinfo_t entinfo; char netname[MAX_NETNAME]; // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { //NOTE: just assume the bot knows where the person is //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); //} } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_CAMPORDER; //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME; //the teammate that requested the camping bs->teammate = client; //not arrived yet bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_FollowMe ================== */ void BotVoiceChat_FollowMe(bot_state_t *bs, int client, int mode) { int areanum; aas_entityinfo_t entinfo; char netname[MAX_NETNAME]; bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //the team mate bs->teammate = client; //last time the team mate was assumed visible bs->teammatevisible_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; //set the ltg type bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_FollowFlagCarrier ================== */ void BotVoiceChat_FollowFlagCarrier(bot_state_t *bs, int client, int mode) { int carrier; carrier = BotTeamFlagCarrier(bs); if (carrier >= 0) BotVoiceChat_FollowMe(bs, carrier, mode); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_ReturnFlag ================== */ void BotVoiceChat_ReturnFlag(bot_state_t *bs, int client, int mode) { //if not in CTF mode if ( gametype != GT_CTF && gametype != GT_CTF_ELIMINATION && gametype != GT_1FCTF ) { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RETURNFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; bs->rushbaseaway_time = 0; BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_StartLeader ================== */ void BotVoiceChat_StartLeader(bot_state_t *bs, int client, int mode) { ClientName(client, bs->teamleader, sizeof(bs->teamleader)); } /* ================== BotVoiceChat_StopLeader ================== */ void BotVoiceChat_StopLeader(bot_state_t *bs, int client, int mode) { char netname[MAX_MESSAGE_SIZE]; if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) { bs->teamleader[0] = '\0'; notleader[client] = qtrue; } } /* ================== BotVoiceChat_WhoIsLeader ================== */ void BotVoiceChat_WhoIsLeader(bot_state_t *bs, int client, int mode) { char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; ClientName(bs->client, netname, sizeof(netname)); //if this bot IS the team leader if (!Q_stricmp(netname, bs->teamleader)) { BotAI_BotInitialChat(bs, "iamteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_STARTLEADER); } } /* ================== BotVoiceChat_WantOnDefense ================== */ void BotVoiceChat_WantOnDefense(bot_state_t *bs, int client, int mode) { char netname[MAX_NETNAME]; int preference; preference = BotGetTeamMateTaskPreference(bs, client); preference &= ~TEAMTP_ATTACKER; preference |= TEAMTP_DEFENDER; BotSetTeamMateTaskPreference(bs, client, preference); // EasyClientName(client, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, client, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotVoiceChat_WantOnOffense ================== */ void BotVoiceChat_WantOnOffense(bot_state_t *bs, int client, int mode) { char netname[MAX_NETNAME]; int preference; preference = BotGetTeamMateTaskPreference(bs, client); preference &= ~TEAMTP_DEFENDER; preference |= TEAMTP_ATTACKER; BotSetTeamMateTaskPreference(bs, client, preference); // EasyClientName(client, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, client, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } void BotVoiceChat_Dummy(bot_state_t *bs, int client, int mode) { } voiceCommand_t voiceCommands[] = { {VOICECHAT_GETFLAG, BotVoiceChat_GetFlag}, {VOICECHAT_OFFENSE, BotVoiceChat_Offense }, {VOICECHAT_DEFEND, BotVoiceChat_Defend }, {VOICECHAT_DEFENDFLAG, BotVoiceChat_DefendFlag }, {VOICECHAT_PATROL, BotVoiceChat_Patrol }, {VOICECHAT_CAMP, BotVoiceChat_Camp }, {VOICECHAT_FOLLOWME, BotVoiceChat_FollowMe }, {VOICECHAT_FOLLOWFLAGCARRIER, BotVoiceChat_FollowFlagCarrier }, {VOICECHAT_RETURNFLAG, BotVoiceChat_ReturnFlag }, {VOICECHAT_STARTLEADER, BotVoiceChat_StartLeader }, {VOICECHAT_STOPLEADER, BotVoiceChat_StopLeader }, {VOICECHAT_WHOISLEADER, BotVoiceChat_WhoIsLeader }, {VOICECHAT_WANTONDEFENSE, BotVoiceChat_WantOnDefense }, {VOICECHAT_WANTONOFFENSE, BotVoiceChat_WantOnOffense }, {NULL, BotVoiceChat_Dummy} }; int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) { int i, voiceOnly, clientNum, color; char *ptr, buf[MAX_MESSAGE_SIZE], *cmd; if (!TeamPlayIsOn()) { return qfalse; } if ( mode == SAY_ALL ) { return qfalse; // don't do anything with voice chats to everyone } Q_strncpyz(buf, voiceChat, sizeof(buf)); cmd = buf; for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; voiceOnly = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; clientNum = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; color = atoi(ptr); if (!BotSameTeam(bs, clientNum)) { return qfalse; } for (i = 0; voiceCommands[i].cmd; i++) { if (!Q_stricmp(cmd, voiceCommands[i].cmd)) { voiceCommands[i].func(bs, clientNum, mode); return qtrue; } } return qfalse; } openarena_0.8.8.orig/code/game/g_svcmds.c0000644000175000017500000002731211656310264017027 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // this file holds commands that can be executed by the server console, but not remote clients #include "g_local.h" /* ============================================================================== PACKET FILTERING You can add or remove addresses from the filter list with: addip removeip The ip address is specified in dot format, and you can use '*' to match any value so you can specify an entire class C network with "addip 192.246.40.*" Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. listip Prints the current list of filters. g_filterban <0 or 1> If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. TTimo NOTE: for persistence, bans are stored in g_banIPs cvar MAX_CVAR_VALUE_STRING The size of the cvar string buffer is limiting the banning to around 20 masks this could be improved by putting some g_banIPs2 g_banIps3 etc. maybe still, you should rely on PB for banning instead ============================================================================== */ typedef struct ipFilter_s { unsigned mask; unsigned compare; } ipFilter_t; #define MAX_IPFILTERS 1024 static ipFilter_t ipFilters[MAX_IPFILTERS]; static int numIPFilters; /* ================= StringToFilter ================= */ static qboolean StringToFilter (char *s, ipFilter_t *f) { char num[128]; int i, j; byte b[4]; byte m[4]; for (i=0 ; i<4 ; i++) { b[i] = 0; m[i] = 0; } for (i=0 ; i<4 ; i++) { if (*s < '0' || *s > '9') { if (*s == '*') // 'match any' { // b[i] and m[i] to 0 s++; if (!*s) break; s++; continue; } G_Printf( "Bad filter address: %s\n", s ); return qfalse; } j = 0; while (*s >= '0' && *s <= '9') { num[j++] = *s++; } num[j] = 0; b[i] = atoi(num); m[i] = 255; if (!*s) break; s++; } f->mask = *(unsigned *)m; f->compare = *(unsigned *)b; return qtrue; } /* ================= UpdateIPBans ================= */ static void UpdateIPBans (void) { byte b[4]; byte m[4]; int i,j; char iplist_final[MAX_CVAR_VALUE_STRING]; char ip[64]; *iplist_final = 0; for (i = 0 ; i < numIPFilters ; i++) { if (ipFilters[i].compare == 0xffffffff) continue; *(unsigned *)b = ipFilters[i].compare; *(unsigned *)m = ipFilters[i].mask; *ip = 0; for (j = 0 ; j < 4 ; j++) { if (m[j]!=255) Q_strcat(ip, sizeof(ip), "*"); else Q_strcat(ip, sizeof(ip), va("%i", b[j])); Q_strcat(ip, sizeof(ip), (j<3) ? "." : " "); } if (strlen(iplist_final)+strlen(ip) < MAX_CVAR_VALUE_STRING) { Q_strcat( iplist_final, sizeof(iplist_final), ip); } else { Com_Printf("g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n"); break; } } trap_Cvar_Set( "g_banIPs", iplist_final ); } /* ================= G_FilterPacket ================= */ qboolean G_FilterPacket (char *from) { int i; unsigned in; byte m[4]; char *p; i = 0; p = from; while (*p && i < 4) { m[i] = 0; while (*p >= '0' && *p <= '9') { m[i] = m[i]*10 + (*p - '0'); p++; } if (!*p || *p == ':') break; i++, p++; } in = *(unsigned *)m; for (i=0 ; i\n"); return; } trap_Argv( 1, str, sizeof( str ) ); AddIP( str ); } /* ================= Svcmd_RemoveIP_f ================= */ void Svcmd_RemoveIP_f (void) { ipFilter_t f; int i; char str[MAX_TOKEN_CHARS]; if ( trap_Argc() < 2 ) { G_Printf("Usage: sv removeip \n"); return; } trap_Argv( 1, str, sizeof( str ) ); if (!StringToFilter (str, &f)) return; for (i=0 ; iinuse ) { continue; } G_Printf("%3i:", e); switch ( check->s.eType ) { case ET_GENERAL: G_Printf("ET_GENERAL "); break; case ET_PLAYER: G_Printf("ET_PLAYER "); break; case ET_ITEM: G_Printf("ET_ITEM "); break; case ET_MISSILE: G_Printf("ET_MISSILE "); break; case ET_MOVER: G_Printf("ET_MOVER "); break; case ET_BEAM: G_Printf("ET_BEAM "); break; case ET_PORTAL: G_Printf("ET_PORTAL "); break; case ET_SPEAKER: G_Printf("ET_SPEAKER "); break; case ET_PUSH_TRIGGER: G_Printf("ET_PUSH_TRIGGER "); break; case ET_TELEPORT_TRIGGER: G_Printf("ET_TELEPORT_TRIGGER "); break; case ET_INVISIBLE: G_Printf("ET_INVISIBLE "); break; case ET_GRAPPLE: G_Printf("ET_GRAPPLE "); break; default: G_Printf("%3i ", check->s.eType); break; } if ( check->classname ) { G_Printf("%s", check->classname); } G_Printf("\n"); } } gclient_t *ClientForString( const char *s ) { gclient_t *cl; int i; int idnum; // numeric values are just slot numbers if ( s[0] >= '0' && s[0] <= '9' ) { idnum = atoi( s ); if ( idnum < 0 || idnum >= level.maxclients ) { Com_Printf( "Bad client slot: %i\n", idnum ); return NULL; } cl = &level.clients[idnum]; if ( cl->pers.connected == CON_DISCONNECTED ) { G_Printf( "Client %i is not connected\n", idnum ); return NULL; } return cl; } // check for a name match for ( i=0 ; i < level.maxclients ; i++ ) { cl = &level.clients[i]; if ( cl->pers.connected == CON_DISCONNECTED ) { continue; } if ( !Q_stricmp( cl->pers.netname, s ) ) { return cl; } } G_Printf( "User %s is not on the server\n", s ); return NULL; } /* =================== Svcmd_ForceTeam_f forceteam =================== */ void Svcmd_ForceTeam_f( void ) { gclient_t *cl; char str[MAX_TOKEN_CHARS]; // find the player trap_Argv( 1, str, sizeof( str ) ); cl = ClientForString( str ); if ( !cl ) { return; } // set the team trap_Argv( 2, str, sizeof( str ) ); SetTeam( &g_entities[cl - level.clients], str ); } void ClientKick_f( void ) { int idnum, i; char str[MAX_TOKEN_CHARS]; trap_Argv( 1, str, sizeof( str ) ); for (i = 0; str[i]; i++) { if (str[i] < '0' || str[i] > '9') { G_Printf("not a valid client number: \"%s\"\n",str); return; } } idnum = atoi( str ); //Local client if( !strcmp( level.clients[idnum].pers.ip, "localhost" ) ) { G_Printf("Kick failed - local player\n"); return; } //Now clientkick has been moved into game, but we still need to find the idnum the server expects.... //FIXME: To fix this, we need a relieble way to generate difference between the server's client number and the game's client numbers //FIXME: This should not depend on the engine's clientkick at all trap_DropClient( idnum, "was kicked" ); //trap_SendConsoleCommand( EXEC_INSERT, va("clientkick %d\n", level.clients[idnum].ps.clientNum) ); } void EndGame_f ( void ) { ExitLevel(); } //KK-OAX Moved this Declaration to g_local.h //char *ConcatArgs( int start ); /*KK-OAX =============== Server Command Table Not Worth Listing Elsewhere ================ */ struct { char *cmd; qboolean dedicated; //if it has to be entered from a dedicated server or RCON void ( *function )( void ); } svcmds[ ] = { { "entityList", qfalse, Svcmd_EntityList_f }, { "forceTeam", qfalse, Svcmd_ForceTeam_f }, { "game_memory", qfalse, Svcmd_GameMem_f }, { "addbot", qfalse, Svcmd_AddBot_f }, { "botlist", qfalse, Svcmd_BotList_f }, { "abort_podium", qfalse, Svcmd_AbortPodium_f }, { "addip", qfalse, Svcmd_AddIP_f }, { "removeip", qfalse, Svcmd_RemoveIP_f }, //KK-OAX Uses wrapper in g_svccmds_ext.c { "listip", qfalse, Svcmd_ListIP_f }, //KK-OAX New { "status", qfalse, Svcmd_Status_f }, { "eject", qfalse, Svcmd_EjectClient_f }, { "dumpuser", qfalse, Svcmd_DumpUser_f }, // don't handle communication commands unless dedicated { "cp", qtrue, Svcmd_CenterPrint_f }, { "say_team", qtrue, Svcmd_TeamMessage_f }, { "say", qtrue, Svcmd_MessageWrapper }, { "chat", qtrue, Svcmd_Chat_f }, /*{ "m", qtrue, Svcmd_MessageWrapper }, { "a", qtrue, Svcmd_MessageWrapper }, { "bp", qtrue, Svcmd_BannerPrint_f }, */ //Shuffle the teams { "shuffle", qfalse, ShuffleTeams }, //Kicks a player by number in the game logic rather than the server number { "clientkick_game", qfalse, ClientKick_f }, { "endgamenow", qfalse, EndGame_f }, }; /* ================= ConsoleCommand ================= */ qboolean ConsoleCommand( void ) { char cmd[ MAX_TOKEN_CHARS ]; int i; trap_Argv( 0, cmd, sizeof( cmd ) ); for( i = 0; i < sizeof( svcmds ) / sizeof( svcmds[ 0 ] ); i++ ) { if( !Q_stricmp( cmd, svcmds[ i ].cmd ) ) { if( svcmds[ i ].dedicated && !g_dedicated.integer ) return qfalse; svcmds[ i ].function( ); return qtrue; } } // KK-OAX Will be enabled when admin is added. // see if this is an admin command if( G_admin_cmd_check( NULL, qfalse ) ) return qtrue; if( g_dedicated.integer ) G_Printf( "unknown command: %s\n", cmd ); return qfalse; } openarena_0.8.8.orig/code/game/g_svcmds_ext.c0000644000175000017500000001347011656310264017707 0ustar smcvsmcv//KK-OAX /* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of the Open Arena source code. Copied from Tremulous under GPL version 2 including any later version. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" /* ============ Svcmd_status_f Does Server Status from Console ============ */ void Svcmd_Status_f( void ) { int i; gclient_t *cl; char userinfo[ MAX_INFO_STRING ]; G_Printf( "slot score ping address rate name\n" ); G_Printf( "---- ----- ---- ------- ---- ----\n" ); for( i = 0, cl = level.clients; i < level.maxclients; i++, cl++ ) { if( cl->pers.connected == CON_DISCONNECTED ) continue; G_Printf( "%-4d ", i ); G_Printf( "%-5d ", cl->ps.persistant[ PERS_SCORE ] ); if( cl->pers.connected == CON_CONNECTING ) G_Printf( "CNCT " ); else G_Printf( "%-4d ", cl->ps.ping ); trap_GetUserinfo( i, userinfo, sizeof( userinfo ) ); G_Printf( "%-21s ", Info_ValueForKey( userinfo, "ip" ) ); G_Printf( "%-8d ", Info_ValueForKey( userinfo, "rate" ) ); G_Printf( "%s\n", cl->pers.netname ); // Info_ValueForKey( userinfo, "name" ) } } /* ============ Svcmd_TeamMessage_f Sends a Chat Message to a Team from the Console ============ */ void Svcmd_TeamMessage_f( void ) { char teamNum[ 2 ]; const char* prefix; team_t team; if( trap_Argc( ) < 3 ) { G_Printf( "usage: say_team \n" ); return; } trap_Argv( 1, teamNum, sizeof( teamNum ) ); team = G_TeamFromString( teamNum ); if( team == TEAM_NUM_TEAMS ) { G_Printf( "say_team: invalid team \"%s\"\n", teamNum ); return; } prefix = BG_TeamName( team ); prefix = va( "[%c] ", toupper( *prefix ) ); G_TeamCommand( team, va( "tchat \"(console): " S_COLOR_CYAN "%s\"", ConcatArgs( 2 ) ) ); G_LogPrintf( "sayteam: %sconsole: " S_COLOR_CYAN "%s\n", prefix, ConcatArgs( 2 ) ); } /* ============ Svcmd_CenterPrint_f Does a CenterPrint from the Console ============ */ void Svcmd_CenterPrint_f( void ) { if( trap_Argc( ) < 2 ) { G_Printf( "usage: cp \n" ); return; } trap_SendServerCommand( -1, va( "cp \"%s\"", ConcatArgs( 1 ) ) ); } /* ============ Svcmd_BannerPrint_f Does a BannerPrint from the Console KK-OAX Commented out in g_svccmds.c, so right now it's useless. ============ */ void Svcmd_BannerPrint_f( void ) { if( trap_Argc( ) < 2 ) { G_Printf( "usage: bp \n" ); return; } trap_SendServerCommand( -1, va( "bp \"%s\"", ConcatArgs( 1 ) ) ); } /* ============ Svcmd_EjectClient_f Kicks a Client from Console KK-OAX, I'm pretty sure this is also done in the "server" portion of the engine code with "kick," but oh well. ============ */ void Svcmd_EjectClient_f( void ) { char *reason, name[ MAX_STRING_CHARS ]; if( trap_Argc( ) < 2 ) { G_Printf( "usage: eject \n" ); return; } trap_Argv( 1, name, sizeof( name ) ); reason = ConcatArgs( 2 ); if( atoi( name ) == -1 ) { int i; for( i = 0; i < level.maxclients; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( level.clients[ i ].pers.localClient ) continue; trap_DropClient( i, reason ); } } else { gclient_t *cl = ClientForString( name ); if( !cl ) return; if( cl->pers.localClient ) { G_Printf( "eject: cannot eject local clients\n" ); return; } trap_DropClient( cl-level.clients, reason ); } } /* ============ Svcmd_DumpUser_f Shows User Info ============ */ void Svcmd_DumpUser_f( void ) { char name[ MAX_STRING_CHARS ], userinfo[ MAX_INFO_STRING ]; char key[ BIG_INFO_KEY ], value[ BIG_INFO_VALUE ]; const char *info; gclient_t *cl; if( trap_Argc( ) != 2 ) { G_Printf( "usage: dumpuser \n" ); return; } trap_Argv( 1, name, sizeof( name ) ); cl = ClientForString( name ); if( !cl ) return; trap_GetUserinfo( cl-level.clients, userinfo, sizeof( userinfo ) ); info = &userinfo[ 0 ]; G_Printf( "userinfo\n--------\n" ); //Info_Print( userinfo ); while( 1 ) { Info_NextPair( &info, key, value ); if( !*info ) return; G_Printf( "%-20s%s\n", key, value ); } } void Svcmd_Chat_f( void ) { trap_SendServerCommand( -1, va( "chat \"%s\"", ConcatArgs( 1 ) ) ); G_LogPrintf("chat: %s\n", ConcatArgs( 1 ) ); } /* ============= Svcmd_ListIP_f Dumb Wrapper for the trap_Send command ============= */ void Svcmd_ListIP_f( void ) { trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" ); } /* ============= Svcmd_MessageWrapper Dumb wrapper for "a" and "m" and "say" ============= */ void Svcmd_MessageWrapper( void ) { char cmd[ 5 ]; trap_Argv( 0, cmd, sizeof( cmd ) ); /*if( !Q_stricmp( cmd, "a" ) ) Cmd_AdminMessage_f( NULL ); else if( !Q_stricmp( cmd, "m" ) ) Cmd_PrivateMessage_f( NULL ); else*/ if( !Q_stricmp( cmd, "say" ) ) G_Say( NULL, NULL, SAY_ALL, ConcatArgs( 1 ) ); } openarena_0.8.8.orig/code/game/ai_cmd.h0000644000175000017500000000252411656310264016441 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_cmd.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ extern int notleader[MAX_CLIENTS]; int BotMatchMessage(bot_state_t *bs, char *message); void BotPrintTeamGoal(bot_state_t *bs); openarena_0.8.8.orig/code/game/g_active.c0000644000175000017500000011324111656310264017000 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" /* =============== G_DamageFeedback Called just before a snapshot is sent to the given player. Totals up all damage and generates both the player_state_t damage values to that client for pain blends and kicks, and global pain sound events for all clients. =============== */ void P_DamageFeedback( gentity_t *player ) { gclient_t *client; float count; vec3_t angles; client = player->client; if ( client->ps.pm_type == PM_DEAD ) { return; } // total points of damage shot at the player this frame count = client->damage_blood + client->damage_armor; if ( count == 0 ) { return; // didn't take any damage } if ( count > 255 ) { count = 255; } // send the information to the client // world damage (falling, slime, etc) uses a special code // to make the blend blob centered instead of positional if ( client->damage_fromWorld ) { client->ps.damagePitch = 255; client->ps.damageYaw = 255; client->damage_fromWorld = qfalse; } else { vectoangles( client->damage_from, angles ); client->ps.damagePitch = angles[PITCH]/360.0 * 256; client->ps.damageYaw = angles[YAW]/360.0 * 256; } // play an apropriate pain sound if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) { player->pain_debounce_time = level.time + 700; G_AddEvent( player, EV_PAIN, player->health ); client->ps.damageEvent++; } client->ps.damageCount = count; // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_knockback = 0; } /* ============= P_WorldEffects Check for lava / slime contents and drowning ============= */ void P_WorldEffects( gentity_t *ent ) { qboolean envirosuit; int waterlevel; if ( ent->client->noclip ) { ent->client->airOutTime = level.time + 12000; // don't need air return; } waterlevel = ent->waterlevel; envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time; // // check for drowning // if ( waterlevel == 3 ) { // envirosuit give air if ( envirosuit ) { ent->client->airOutTime = level.time + 10000; } // if out of air, start drowning if ( ent->client->airOutTime < level.time) { // drown! ent->client->airOutTime += 1000; if ( ent->health > 0 ) { // take more damage the longer underwater ent->damage += 2; if (ent->damage > 15) ent->damage = 15; // don't play a normal pain sound ent->pain_debounce_time = level.time + 200; G_Damage (ent, NULL, NULL, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_WATER); } } } else { ent->client->airOutTime = level.time + 12000; ent->damage = 2; } // // check for sizzle damage (move to pmove?) // if (waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { if (ent->health > 0 && ent->pain_debounce_time <= level.time ) { if ( envirosuit ) { G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 ); } else { if (ent->watertype & CONTENTS_LAVA) { G_Damage (ent, NULL, NULL, NULL, NULL, 30*waterlevel, 0, MOD_LAVA); } if (ent->watertype & CONTENTS_SLIME) { G_Damage (ent, NULL, NULL, NULL, NULL, 10*waterlevel, 0, MOD_SLIME); } } } } } /* =============== G_SetClientSound =============== */ void G_SetClientSound( gentity_t *ent ) { if( ent->s.eFlags & EF_TICKING ) { ent->client->ps.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav"); } else if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { ent->client->ps.loopSound = level.snd_fry; } else { ent->client->ps.loopSound = 0; } } //============================================================== /* ============== ClientImpacts ============== */ void ClientImpacts( gentity_t *ent, pmove_t *pm ) { int i, j; trace_t trace; gentity_t *other; memset( &trace, 0, sizeof( trace ) ); for (i=0 ; inumtouch ; i++) { for (j=0 ; jtouchents[j] == pm->touchents[i] ) { break; } } if (j != i) { continue; // duplicated } other = &g_entities[ pm->touchents[i] ]; if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, other, &trace ); } if ( !other->touch ) { continue; } other->touch( other, ent, &trace ); } } /* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers( gentity_t *ent ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; static vec3_t range = { 40, 40, 52 }; if ( !ent->client ) { return; } //ELIMINATION LMS // dead clients don't activate triggers! The reason our pm_spectators can't do anything if ( ent->client->ps.stats[STAT_HEALTH] <= 0 && ent->client->ps.pm_type != PM_SPECTATOR) { return; } VectorSubtract( ent->client->ps.origin, range, mins ); VectorAdd( ent->client->ps.origin, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->absmin, because that has a one unit pad VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); for ( i=0 ; itouch && !ent->touch ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } // ignore most entities if a spectator if ( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) || ent->client->ps.pm_type == PM_SPECTATOR ) { if ( hit->s.eType != ET_TELEPORT_TRIGGER && // this is ugly but adding a new ET_? type will // most likely cause network incompatibilities //We need to stop eliminated players from opening doors somewhere else /Sago007 20070814 hit->touch != Touch_DoorTrigger ) { continue; } } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if ( hit->s.eType == ET_ITEM ) { if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { continue; } } else { if ( !trap_EntityContact( mins, maxs, hit ) ) { continue; } } memset( &trace, 0, sizeof(trace) ); if ( hit->touch ) { hit->touch (hit, ent, &trace); } if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, hit, &trace ); } } // if we didn't touch a jump pad this pmove frame if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) { ent->client->ps.jumppad_frame = 0; ent->client->ps.jumppad_ent = 0; } } /* ================= SpectatorThink ================= */ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { pmove_t pm; gclient_t *client; client = ent->client; if ( ( g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && client->sess.spectatorState != SPECTATOR_FOLLOW && g_elimination_lockspectator.integer>1 && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { Cmd_FollowCycle_f(ent); } if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) { client->ps.pm_type = PM_SPECTATOR; client->ps.speed = 400; // faster than normal // set up for pmove memset (&pm, 0, sizeof(pm)); pm.ps = &client->ps; pm.cmd = *ucmd; pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; // perform a pmove Pmove (&pm); // save results of pmove VectorCopy( client->ps.origin, ent->s.origin ); G_TouchTriggers( ent ); trap_UnlinkEntity( ent ); } /* Stopped players from going into follow mode in B5, should be fixed in B9 if(ent->client->sess.sessionTeam != TEAM_SPECTATOR && g_gametype.integer>=GT_ELIMINATION && g_gametype.integer<=GT_LMS) return; */ client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; //KK-OAX Changed to keep followcycle functional // attack button cycles through spectators if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) { Cmd_FollowCycle_f( ent ); } if ( ( client->buttons & BUTTON_USE_HOLDABLE ) && ! ( client->oldbuttons & BUTTON_USE_HOLDABLE ) ) { if ( ( g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && g_elimination_lockspectator.integer>1 && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { return; } StopFollowing(ent); } } /* ================= ClientInactivityTimer Returns qfalse if the client is dropped ================= */ qboolean ClientInactivityTimer( gclient_t *client ) { if ( ! g_inactivity.integer ) { // give everyone some time, so if the operator sets g_inactivity during // gameplay, everyone isn't kicked client->inactivityTime = level.time + 60 * 1000; client->inactivityWarning = qfalse; } else if ( client->pers.cmd.forwardmove || client->pers.cmd.rightmove || client->pers.cmd.upmove || (client->pers.cmd.buttons & BUTTON_ATTACK) ) { client->inactivityTime = level.time + g_inactivity.integer * 1000; client->inactivityWarning = qfalse; } else if ( !client->pers.localClient ) { if ( level.time > client->inactivityTime ) { trap_DropClient( client - level.clients, "Dropped due to inactivity" ); return qfalse; } if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) { client->inactivityWarning = qtrue; trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); } } return qtrue; } /* ================== ClientTimerActions Actions that happen once a second ================== */ void ClientTimerActions( gentity_t *ent, int msec ) { gclient_t *client; int maxHealth; client = ent->client; client->timeResidual += msec; while ( client->timeResidual >= 1000 ) { client->timeResidual -= 1000; //Stop in elimination!!! if (client->ps.pm_flags & PMF_ELIMWARMUP) continue; // regenerate if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { maxHealth = client->ps.stats[STAT_MAX_HEALTH] / 2; } else if ( client->ps.powerups[PW_REGEN] ) { maxHealth = client->ps.stats[STAT_MAX_HEALTH]; } else { maxHealth = 0; } if( maxHealth ) { if ( ent->health < maxHealth ) { ent->health += 15; if ( ent->health > maxHealth * 1.1 ) { ent->health = maxHealth * 1.1; } G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); } else if ( ent->health < maxHealth * 2) { ent->health += 5; if ( ent->health > maxHealth * 2 ) { ent->health = maxHealth * 2; } G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); } } else { // count down health when over max if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) { ent->health--; } //Start killing players in LMS, if we are in overtime if(g_elimination_roundtime.integer&&g_gametype.integer==GT_LMS && TeamHealthCount( -1, TEAM_FREE ) != ent->health &&(level.roundNumber==level.roundNumberStarted)&&(level.time>=level.roundStartTime+1000*g_elimination_roundtime.integer)) { ent->damage=5; G_Damage (ent, NULL, NULL, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_UNKNOWN); } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] ) { ent->health+=g_regen.integer; if(ent->health>client->ps.stats[STAT_MAX_HEALTH]) ent->health= client->ps.stats[STAT_MAX_HEALTH]; } } // count down armor when over max if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) { client->ps.stats[STAT_ARMOR]--; } } if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { int w, max, inc, t, i; int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_NAILGUN,WP_PROX_LAUNCHER,WP_CHAINGUN}; int weapCount = sizeof(weapList) / sizeof(int); // for (i = 0; i < weapCount; i++) { w = weapList[i]; switch(w) { case WP_MACHINEGUN: max = 50; inc = 4; t = 1000; break; case WP_SHOTGUN: max = 10; inc = 1; t = 1500; break; case WP_GRENADE_LAUNCHER: max = 10; inc = 1; t = 2000; break; case WP_ROCKET_LAUNCHER: max = 10; inc = 1; t = 1750; break; case WP_LIGHTNING: max = 50; inc = 5; t = 1500; break; case WP_RAILGUN: max = 10; inc = 1; t = 1750; break; case WP_PLASMAGUN: max = 50; inc = 5; t = 1500; break; case WP_BFG: max = 10; inc = 1; t = 4000; break; case WP_NAILGUN: max = 10; inc = 1; t = 1250; break; case WP_PROX_LAUNCHER: max = 5; inc = 1; t = 2000; break; case WP_CHAINGUN: max = 100; inc = 5; t = 1000; break; default: max = 0; inc = 0; t = 1000; break; } client->ammoTimes[w] += msec; if ( client->ps.ammo[w] >= max ) { client->ammoTimes[w] = 0; } if ( client->ammoTimes[w] >= t ) { while ( client->ammoTimes[w] >= t ) client->ammoTimes[w] -= t; client->ps.ammo[w] += inc; if ( client->ps.ammo[w] > max ) { client->ps.ammo[w] = max; } } } } } /* ==================== ClientIntermissionThink ==================== */ void ClientIntermissionThink( gclient_t *client ) { client->ps.eFlags &= ~EF_TALK; client->ps.eFlags &= ~EF_FIRING; // the level will exit when everyone wants to or after timeouts if( g_entities[client->ps.clientNum].r.svFlags & SVF_BOT ) return; //Bots cannot mark themself as ready // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = client->pers.cmd.buttons; if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) { // this used to be an ^1 but once a player says ready, it should stick client->readyToExit = 1; } } /* ================ ClientEvents Events will be passed on to the clients for presentation, but any server game effects are handled here ================ */ void ClientEvents( gentity_t *ent, int oldEventSequence ) { int i, j; int event; gclient_t *client; int damage; vec3_t dir; vec3_t origin, angles; // qboolean fired; gitem_t *item; gentity_t *drop; client = ent->client; if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) { oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS; } for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) { event = client->ps.events[ i & (MAX_PS_EVENTS-1) ]; switch ( event ) { case EV_FALL_MEDIUM: case EV_FALL_FAR: if ( ent->s.eType != ET_PLAYER ) { break; // not in the player model } if ( g_dmflags.integer & DF_NO_FALLING ) { break; } if ( event == EV_FALL_FAR ) { damage = 10; } else { damage = 5; } VectorSet (dir, 0, 0, 1); ent->pain_debounce_time = level.time + 200; // no normal pain sound G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING); break; case EV_FIRE_WEAPON: FireWeapon( ent ); break; case EV_USE_ITEM1: // teleporter // drop flags in CTF item = NULL; j = 0; if ( ent->client->ps.powerups[ PW_REDFLAG ] ) { item = BG_FindItemForPowerup( PW_REDFLAG ); j = PW_REDFLAG; } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) { item = BG_FindItemForPowerup( PW_BLUEFLAG ); j = PW_BLUEFLAG; } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) { item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); j = PW_NEUTRALFLAG; } if ( item ) { drop = Drop_Item( ent, item, 0 ); // decide how many seconds it has left drop->count = ( ent->client->ps.powerups[ j ] - level.time ) / 1000; if ( drop->count < 1 ) { drop->count = 1; } ent->client->ps.powerups[ j ] = 0; } if ( g_gametype.integer == GT_HARVESTER ) { if ( ent->client->ps.generic1 > 0 ) { if ( ent->client->sess.sessionTeam == TEAM_RED ) { item = BG_FindItem( "Blue Cube" ); } else { item = BG_FindItem( "Red Cube" ); } if ( item ) { for ( j = 0; j < ent->client->ps.generic1; j++ ) { drop = Drop_Item( ent, item, 0 ); if ( ent->client->sess.sessionTeam == TEAM_RED ) { drop->spawnflags = TEAM_BLUE; } else { drop->spawnflags = TEAM_RED; } } } ent->client->ps.generic1 = 0; } } SelectSpawnPoint( ent->client->ps.origin, origin, angles ); TeleportPlayer( ent, origin, angles ); break; case EV_USE_ITEM2: // medkit ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25; break; case EV_USE_ITEM3: // kamikaze // make sure the invulnerability is off ent->client->invulnerabilityTime = 0; // start the kamikze G_StartKamikaze( ent ); break; case EV_USE_ITEM4: // portal if( ent->client->portalID ) { DropPortalSource( ent ); } else { DropPortalDestination( ent ); } break; case EV_USE_ITEM5: // invulnerability ent->client->invulnerabilityTime = level.time + 10000; break; default: break; } } } /* ============== StuckInOtherClient ============== */ static int StuckInOtherClient(gentity_t *ent) { int i; gentity_t *ent2; ent2 = &g_entities[0]; for ( i = 0; i < MAX_CLIENTS; i++, ent2++ ) { if ( ent2 == ent ) { continue; } if ( !ent2->inuse ) { continue; } if ( !ent2->client ) { continue; } if ( ent2->health <= 0 ) { continue; } // if (ent2->r.absmin[0] > ent->r.absmax[0]) continue; if (ent2->r.absmin[1] > ent->r.absmax[1]) continue; if (ent2->r.absmin[2] > ent->r.absmax[2]) continue; if (ent2->r.absmax[0] < ent->r.absmin[0]) continue; if (ent2->r.absmax[1] < ent->r.absmin[1]) continue; if (ent2->r.absmax[2] < ent->r.absmin[2]) continue; return qtrue; } return qfalse; } void BotTestSolid(vec3_t origin); /* ============== SendPendingPredictableEvents ============== */ void SendPendingPredictableEvents( playerState_t *ps ) { gentity_t *t; int event, seq; int extEvent, number; // if there are still events pending if ( ps->entityEventSequence < ps->eventSequence ) { // create a temporary entity for this event which is sent to everyone // except the client who generated the event seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); // set external event to zero before calling BG_PlayerStateToEntityState extEvent = ps->externalEvent; ps->externalEvent = 0; // create temporary entity for event t = G_TempEntity( ps->origin, event ); number = t->s.number; BG_PlayerStateToEntityState( ps, &t->s, qtrue ); t->s.number = number; t->s.eType = ET_EVENTS + event; t->s.eFlags |= EF_PLAYER_EVENT; t->s.otherEntityNum = ps->clientNum; // send to everyone except the client who generated the event t->r.svFlags |= SVF_NOTSINGLECLIENT; t->r.singleClient = ps->clientNum; // set back external event ps->externalEvent = extEvent; } } /* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if ( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } //Here comes the unlagged bit! //unlagged - backward reconciliation #4 // frameOffset should be about the number of milliseconds into a frame // this command packet was received, depending on how fast the server // does a G_RunFrame() client->frameOffset = trap_Milliseconds() - level.frameStartTime; //unlagged - backward reconciliation #4 //unlagged - lag simulation #3 // if the client wants to simulate outgoing packet loss /* if ( client->pers.plOut ) { // see if a random value is below the threshhold float thresh = (float)client->pers.plOut / 100.0f; if ( random() < thresh ) { // do nothing at all if it is - this is a lost command return; } }*/ //unlagged - lag simulation #3 //unlagged - true ping // save the estimated ping in a queue for averaging later // we use level.previousTime to account for 50ms lag correction // besides, this will turn out numbers more like what players are used to client->pers.pingsamples[client->pers.samplehead] = level.previousTime + client->frameOffset - ucmd->serverTime; client->pers.samplehead++; if ( client->pers.samplehead >= NUM_PING_SAMPLES ) { client->pers.samplehead -= NUM_PING_SAMPLES; } // initialize the real ping if ( g_truePing.integer ) { int i, sum = 0; // get an average of the samples we saved up for ( i = 0; i < NUM_PING_SAMPLES; i++ ) { sum += client->pers.pingsamples[i]; } client->pers.realPing = sum / NUM_PING_SAMPLES; } else { // if g_truePing is off, use the normal ping client->pers.realPing = client->ps.ping; } //unlagged - true ping //unlagged - lag simulation #2 // keep a queue of past commands /* client->pers.cmdqueue[client->pers.cmdhead] = client->pers.cmd; client->pers.cmdhead++; if ( client->pers.cmdhead >= MAX_LATENT_CMDS ) { client->pers.cmdhead -= MAX_LATENT_CMDS; } // if the client wants latency in commands (client-to-server latency) if ( client->pers.latentCmds ) { // save the actual command time int time = ucmd->serverTime; // find out which index in the queue we want int cmdindex = client->pers.cmdhead - client->pers.latentCmds - 1; while ( cmdindex < 0 ) { cmdindex += MAX_LATENT_CMDS; } // read in the old command client->pers.cmd = client->pers.cmdqueue[cmdindex]; // adjust the real ping to reflect the new latency client->pers.realPing += time - ucmd->serverTime; }*/ //unlagged - lag simulation #2 //unlagged - backward reconciliation #4 // save the command time *before* pmove_fixed messes with the serverTime, // and *after* lag simulation messes with it :) // attackTime will be used for backward reconciliation later (time shift) client->attackTime = ucmd->serverTime; //unlagged - backward reconciliation #4 //unlagged - smooth clients #1 // keep track of this for later - we'll use this to decide whether or not // to send extrapolated positions for this client client->lastUpdateFrame = level.framenum; //unlagged - smooth clients #1 //unlagged - lag simulation #1 // if the client is adding latency to received snapshots (server-to-client latency) /*if ( client->pers.latentSnaps ) { // adjust the real ping client->pers.realPing += client->pers.latentSnaps * (1000 / sv_fps.integer); // adjust the attack time so backward reconciliation will work client->attackTime -= client->pers.latentSnaps * (1000 / sv_fps.integer); }*/ //unlagged - lag simulation #1 //unlagged - true ping // make sure the true ping is over 0 - with cl_timenudge it can be less if ( client->pers.realPing < 0 ) { client->pers.realPing = 0; } //unlagged - true ping msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { return; } if ( msec > 200 ) { msec = 200; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if ( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if ( (client->sess.sessionTeam == TEAM_SPECTATOR) || client->isEliminated ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { return; } SpectatorThink( ent, ucmd ); return; } // check for inactivity timer, but never drop the local client of a non-dedicated server if ( !ClientInactivityTimer( client ) ) { return; } // clear the rewards if time if ( level.time > client->rewardTime ) { client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); } if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { client->ps.pm_type = PM_DEAD; } else { client->ps.pm_type = PM_NORMAL; } client->ps.gravity = g_gravity.value*g_gravityModifier.value; // set speed client->ps.speed = g_speed.value; if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { client->ps.speed *= 1.5; } else if ( client->ps.powerups[PW_HASTE] ) { client->ps.speed *= 1.3; } // Let go of the hook if we aren't firing if ( client->ps.weapon == WP_GRAPPLING_HOOK && client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) { Weapon_HookFree(client->hook); } // set up for pmove oldEventSequence = client->ps.eventSequence; memset (&pm, 0, sizeof(pm)); // check for the hit-scan gauntlet, don't let the action // go through as an attack unless it actually hits something if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) && ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) { pm.gauntletHit = CheckGauntletAttack( ent ); } if ( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } // check for invulnerability expansion before doing the Pmove if (client->ps.powerups[PW_INVULNERABILITY] ) { if ( !(client->ps.pm_flags & PMF_INVULEXPAND) ) { vec3_t mins = { -42, -42, -42 }; vec3_t maxs = { 42, 42, 42 }; vec3_t oldmins, oldmaxs; VectorCopy (ent->r.mins, oldmins); VectorCopy (ent->r.maxs, oldmaxs); // expand VectorCopy (mins, ent->r.mins); VectorCopy (maxs, ent->r.maxs); trap_LinkEntity(ent); // check if this would get anyone stuck in this player if ( !StuckInOtherClient(ent) ) { // set flag so the expanded size will be set in PM_CheckDuck client->ps.pm_flags |= PMF_INVULEXPAND; } // set back VectorCopy (oldmins, ent->r.mins); VectorCopy (oldmaxs, ent->r.maxs); trap_LinkEntity(ent); } } pm.ps = &client->ps; pm.cmd = *ucmd; if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP; } else { pm.tracemask = MASK_PLAYERSOLID; } pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; pm.pmove_float = pmove_float.integer; pm.pmove_flags = g_dmflags.integer; VectorCopy( client->ps.origin, client->oldOrigin ); #ifdef MISSIONPACK if (level.intermissionQueued != 0 && g_singlePlayer.integer) { if ( level.time - level.intermissionQueued >= 1000 ) { pm.cmd.buttons = 0; pm.cmd.forwardmove = 0; pm.cmd.rightmove = 0; pm.cmd.upmove = 0; if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) { trap_SendConsoleCommand( EXEC_APPEND, "centerview\n"); } ent->client->ps.pm_type = PM_SPINTERMISSION; } } #endif Pmove (&pm); // save results of pmove if ( ent->client->ps.eventSequence != oldEventSequence ) { ent->eventTime = level.time; } //unlagged - smooth clients #2 // clients no longer do extrapolation if cg_smoothClients is 1, because // skip correction is all handled server-side now // since that's the case, it makes no sense to store the extra info // in the client's snapshot entity, so let's save a little bandwidth /* if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { */ BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); // } //unlagged - smooth clients #2 SendPendingPredictableEvents( &ent->client->ps ); if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { client->fireHeld = qfalse; // for grapple } // use the snapped origin for linking so it matches client predicted versions VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy (pm.mins, ent->r.mins); VectorCopy (pm.maxs, ent->r.maxs); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents( ent, oldEventSequence ); // link entity now, after any personal teleporters have been used trap_LinkEntity (ent); if ( !ent->client->noclip ) { G_TouchTriggers( ent ); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); //test for solid areas in the AAS file BotTestAAS(ent->r.currentOrigin); // touch other objects ClientImpacts( ent, &pm ); // save results of triggers and client events if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; } // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // check for respawning if ( client->ps.stats[STAT_HEALTH] <= 0 ) { // wait for the attack button to be pressed // forcerespawn is to prevent users from waiting out powerups // In Last man standing, we force a quick respawn, since // the player must be able to loose health // pressing attack or use is the normal respawn method if ( ( level.time > client->respawnTime ) && ( ( ( g_forcerespawn.integer > 0 ) && ( level.time - client->respawnTime > g_forcerespawn.integer * 1000 ) ) || ( ( ( g_gametype.integer == GT_LMS ) || ( g_gametype.integer == GT_ELIMINATION ) || ( g_gametype.integer == GT_CTF_ELIMINATION ) ) && ( level.time - client->respawnTime > 0 ) ) || ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) ) ) { ClientRespawn( ent ); } return; } if ( pm.waterlevel <= 1 && pm.ps->groundEntityNum!=ENTITYNUM_NONE && client->lastSentFlyingTime+500>level.time) { if ( ! (pm.ps->pm_flags & PMF_TIME_KNOCKBACK) ) { client->lastSentFlying = -1; } } // perform once-a-second actions ClientTimerActions( ent, msec ); } /* ================== ClientThink A new command has arrived from the client ================== */ void ClientThink( int clientNum ) { gentity_t *ent; ent = g_entities + clientNum; trap_GetUsercmd( clientNum, &ent->client->pers.cmd ); //Unlagged: commented out // mark the time we got info, so we can display the // phone jack if they don't get any for a while //ent->client->lastCmdTime = level.time; if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { ClientThink_real( ent ); } } void G_RunClient( gentity_t *ent ) { if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { return; } ent->client->pers.cmd.serverTime = level.time; ClientThink_real( ent ); } /* ================== SpectatorClientEndFrame ================== */ void SpectatorClientEndFrame( gentity_t *ent ) { gclient_t *cl; int i, preservedScore[MAX_PERSISTANT]; //for keeping in elimination // if we are doing a chase cam or a remote view, grab the latest info if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { int clientNum, flags; clientNum = ent->client->sess.spectatorClient; // team follow1 and team follow2 go to whatever clients are playing if ( clientNum == -1 ) { clientNum = level.follow1; } else if ( clientNum == -2 ) { clientNum = level.follow2; } if ( clientNum >= 0 ) { cl = &level.clients[ clientNum ]; if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) { flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED)); //this is here LMS/Elimination goes wrong with player follow if(ent->client->sess.sessionTeam!=TEAM_SPECTATOR){ for(i = 0; i < MAX_PERSISTANT; i++) preservedScore[i] = ent->client->ps.persistant[i]; ent->client->ps = cl->ps; for(i = 0; i < MAX_PERSISTANT; i++) ent->client->ps.persistant[i] = preservedScore[i]; } else ent->client->ps = cl->ps; ent->client->ps.pm_flags |= PMF_FOLLOW; ent->client->ps.eFlags = flags; return; } else { // drop them to free spectators unless they are dedicated camera followers if ( ent->client->sess.spectatorClient >= 0 ) { ent->client->sess.spectatorState = SPECTATOR_FREE; ClientBegin( ent->client - level.clients ); } } } } if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { ent->client->ps.pm_flags |= PMF_SCOREBOARD; } else { ent->client->ps.pm_flags &= ~PMF_SCOREBOARD; } } /* ============== ClientEndFrame Called at the end of each server frame for each connected client A fast client will have multiple ClientThink for each ClientEdFrame, while a slow client may have multiple ClientEndFrame between ClientThink. ============== */ void ClientEndFrame( gentity_t *ent ) { int i; clientPersistant_t *pers; //unlagged - smooth clients #1 int frames; //unlagged - smooth clients #1 if ( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) || ent->client->isEliminated ) { SpectatorClientEndFrame( ent ); return; } pers = &ent->client->pers; // turn off any expired powerups for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ent->client->ps.powerups[ i ] < level.time ) { ent->client->ps.powerups[ i ] = 0; } } // set powerup for player animation if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { ent->client->ps.powerups[PW_GUARD] = level.time; } if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { ent->client->ps.powerups[PW_SCOUT] = level.time; } if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_DOUBLER ) { ent->client->ps.powerups[PW_DOUBLER] = level.time; } if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { ent->client->ps.powerups[PW_AMMOREGEN] = level.time; } if ( ent->client->invulnerabilityTime > level.time ) { ent->client->ps.powerups[PW_INVULNERABILITY] = level.time; } // save network bandwidth #if 0 if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) { // FIXME: this must change eventually for non-sync demo recording VectorClear( ent->client->ps.viewangles ); } #endif // // If the end of unit layout is displayed, don't give // the player any normal movement attributes // if ( level.intermissiontime ) { return; } // burn from lava, etc P_WorldEffects (ent); // apply all the damage taken this frame P_DamageFeedback (ent); //Unlagged: Commented out // add the EF_CONNECTION flag if we haven't gotten commands recently /*if ( level.time - ent->client->lastCmdTime > 1000 ) { ent->s.eFlags |= EF_CONNECTION; } else { ent->s.eFlags &= ~EF_CONNECTION; }*/ ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... G_SetClientSound (ent); //Unlagged: Always do the else clause // set the latest infor /* if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { */ BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); // } SendPendingPredictableEvents( &ent->client->ps ); //unlagged - smooth clients #1 // mark as not missing updates initially ent->client->ps.eFlags &= ~EF_CONNECTION; // see how many frames the client has missed frames = level.framenum - ent->client->lastUpdateFrame - 1; // don't extrapolate more than two frames if ( frames > 2 ) { frames = 2; // if they missed more than two in a row, show the phone jack ent->client->ps.eFlags |= EF_CONNECTION; ent->s.eFlags |= EF_CONNECTION; } // did the client miss any frames? if ( frames > 0 && g_smoothClients.integer ) { // yep, missed one or more, so extrapolate the player's movement G_PredictPlayerMove( ent, (float)frames / sv_fps.integer ); // save network bandwidth SnapVector( ent->s.pos.trBase ); } //unlagged - smooth clients #1 //unlagged - backward reconciliation #1 // store the client's position for backward reconciliation later G_StoreHistory( ent ); //unlagged - backward reconciliation #1 // set the bit for the reachability area the client is currently in // i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin ); // ent->client->areabits[i >> 3] |= 1 << (i & 7); } openarena_0.8.8.orig/code/game/g_combat.c0000644000175000017500000012707411661272471017006 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_combat.c #include "g_local.h" #include "challenges.h" /* ============ ScorePlum ============ */ void ScorePlum( gentity_t *ent, vec3_t origin, int score ) { gentity_t *plum; plum = G_TempEntity( origin, EV_SCOREPLUM ); // only send this temp entity to a single client plum->r.svFlags |= SVF_SINGLECLIENT; plum->r.singleClient = ent->s.number; // plum->s.otherEntityNum = ent->s.number; plum->s.time = score; } /* ============ AddScore Adds score to both the client and his team ============ */ void AddScore( gentity_t *ent, vec3_t origin, int score ) { int i; if ( !ent->client ) { return; } // no scoring during pre-match warmup if ( level.warmupTime ) { return; } //No scoring during intermission if ( level.intermissiontime ) { return; } // show score plum if( level.numNonSpectatorClients<3 && score < 0 && (g_gametype.integerclient->ps.persistant[PERS_SCORE] += score; if ( g_gametype.integer == GT_TEAM ) { int team = ent->client->ps.persistant[PERS_TEAM]; level.teamScores[ team ] += score; G_LogPrintf("TeamScore: %i %i: Team %d now has %d points\n", team, level.teamScores[ team ], team, level.teamScores[ team ] ); } } G_LogPrintf("PlayerScore: %i %i: %s now has %d points\n", ent->s.number, ent->client->ps.persistant[PERS_SCORE], ent->client->pers.netname, ent->client->ps.persistant[PERS_SCORE] ); CalculateRanks(); } /* ================= TossClientItems Toss the weapon and powerups for the killed player ================= */ void TossClientItems( gentity_t *self ) { gitem_t *item; int weapon; float angle; int i; gentity_t *drop; // drop the weapon if not a gauntlet or machinegun weapon = self->s.weapon; //Never drop in elimination or last man standing mode! if( g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_LMS) return; // make a special check to see if they are changing to a new // weapon that isn't the mg or gauntlet. Without this, a client // can pick up a weapon, be killed, and not drop the weapon because // their weapon change hasn't completed yet and they are still holding the MG. if ( weapon == WP_MACHINEGUN || weapon == WP_GRAPPLING_HOOK ) { if ( self->client->ps.weaponstate == WEAPON_DROPPING ) { weapon = self->client->pers.cmd.weapon; } if ( !( self->client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { weapon = WP_NONE; } } if (g_instantgib.integer || g_rockets.integer || g_gametype.integer == GT_CTF_ELIMINATION || g_elimination_allgametypes.integer){ //Nothing! } else if ( weapon > WP_MACHINEGUN && weapon != WP_GRAPPLING_HOOK && self->client->ps.ammo[ weapon ] ) { // find the item type for this weapon item = BG_FindItemForWeapon( weapon ); // spawn the item Drop_Item( self, item, 0 ); } // drop all the powerups if not in teamplay if ( g_gametype.integer != GT_TEAM ) { angle = 45; for ( i = 1 ; i < PW_NUM_POWERUPS ; i++ ) { if ( self->client->ps.powerups[ i ] > level.time ) { item = BG_FindItemForPowerup( i ); if ( !item ) { continue; } drop = Drop_Item( self, item, angle ); // decide how many seconds it has left drop->count = ( self->client->ps.powerups[ i ] - level.time ) / 1000; if ( drop->count < 1 ) { drop->count = 1; } angle += 45; } } } } /* ================= TossClientCubes ================= */ extern gentity_t *neutralObelisk; void TossClientCubes( gentity_t *self ) { gitem_t *item; gentity_t *drop; vec3_t velocity; vec3_t angles; vec3_t origin; self->client->ps.generic1 = 0; // this should never happen but we should never // get the server to crash due to skull being spawned in if (!G_EntitiesFree()) { return; } if( self->client->sess.sessionTeam == TEAM_RED ) { item = BG_FindItem( "Red Cube" ); } else { item = BG_FindItem( "Blue Cube" ); } angles[YAW] = (float)(level.time % 360); angles[PITCH] = 0; // always forward angles[ROLL] = 0; AngleVectors( angles, velocity, NULL, NULL ); VectorScale( velocity, 150, velocity ); velocity[2] += 200 + crandom() * 50; if( neutralObelisk ) { VectorCopy( neutralObelisk->s.pos.trBase, origin ); origin[2] += 44; } else { VectorClear( origin ) ; } drop = LaunchItem( item, origin, velocity ); drop->nextthink = level.time + g_cubeTimeout.integer * 1000; drop->think = G_FreeEntity; drop->spawnflags = self->client->sess.sessionTeam; } /* ================= TossClientPersistantPowerups ================= */ void TossClientPersistantPowerups( gentity_t *ent ) { gentity_t *powerup; if( !ent->client ) { return; } if( !ent->client->persistantPowerup ) { return; } powerup = ent->client->persistantPowerup; powerup->r.svFlags &= ~SVF_NOCLIENT; powerup->s.eFlags &= ~EF_NODRAW; powerup->r.contents = CONTENTS_TRIGGER; trap_LinkEntity( powerup ); ent->client->ps.stats[STAT_PERSISTANT_POWERUP] = 0; ent->client->persistantPowerup = NULL; } /* ================== LookAtKiller ================== */ void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker ) { vec3_t dir; //vec3_t angles; if ( attacker && attacker != self ) { VectorSubtract (attacker->s.pos.trBase, self->s.pos.trBase, dir); } else if ( inflictor && inflictor != self ) { VectorSubtract (inflictor->s.pos.trBase, self->s.pos.trBase, dir); } else { self->client->ps.stats[STAT_DEAD_YAW] = self->s.angles[YAW]; return; } self->client->ps.stats[STAT_DEAD_YAW] = vectoyaw ( dir ); /*angles[YAW] =*/ vectoyaw ( dir ); //angles[PITCH] = 0; //angles[ROLL] = 0; } /* ================== GibEntity ================== */ void GibEntity( gentity_t *self, int killer ) { gentity_t *ent; int i; //if this entity still has kamikaze if (self->s.eFlags & EF_KAMIKAZE) { // check if there is a kamikaze timer around for this owner for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; if (!ent->inuse) continue; if (ent->activator != self) continue; if (strcmp(ent->classname, "kamikaze timer")) continue; G_FreeEntity(ent); break; } } G_AddEvent( self, EV_GIB_PLAYER, killer ); self->takedamage = qfalse; self->s.eType = ET_INVISIBLE; self->r.contents = 0; } /* ================== body_die ================== */ void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { if ( self->health > GIB_HEALTH ) { return; } if ( !g_blood.integer ) { self->health = GIB_HEALTH+1; return; } GibEntity( self, 0 ); } // these are just for logging, the client prints its own messages char *modNames[] = { "MOD_UNKNOWN", "MOD_SHOTGUN", "MOD_GAUNTLET", "MOD_MACHINEGUN", "MOD_GRENADE", "MOD_GRENADE_SPLASH", "MOD_ROCKET", "MOD_ROCKET_SPLASH", "MOD_PLASMA", "MOD_PLASMA_SPLASH", "MOD_RAILGUN", "MOD_LIGHTNING", "MOD_BFG", "MOD_BFG_SPLASH", "MOD_WATER", "MOD_SLIME", "MOD_LAVA", "MOD_CRUSH", "MOD_TELEFRAG", "MOD_FALLING", "MOD_SUICIDE", "MOD_TARGET_LASER", "MOD_TRIGGER_HURT", "MOD_NAIL", "MOD_CHAINGUN", "MOD_PROXIMITY_MINE", "MOD_KAMIKAZE", "MOD_JUICED", "MOD_GRAPPLE" }; /* ================== Kamikaze_DeathActivate ================== */ void Kamikaze_DeathActivate( gentity_t *ent ) { G_StartKamikaze(ent); G_FreeEntity(ent); } /* ================== Kamikaze_DeathTimer ================== */ void Kamikaze_DeathTimer( gentity_t *self ) { gentity_t *ent; ent = G_Spawn(); ent->classname = "kamikaze timer"; VectorCopy(self->s.pos.trBase, ent->s.pos.trBase); ent->r.svFlags |= SVF_NOCLIENT; ent->think = Kamikaze_DeathActivate; ent->nextthink = level.time + 5 * 1000; ent->activator = self; } /* ================== CheckAlmostCapture ================== */ void CheckAlmostCapture( gentity_t *self, gentity_t *attacker ) { gentity_t *ent; vec3_t dir; char *classname; // if this player was carrying a flag if ( self->client->ps.powerups[PW_REDFLAG] || self->client->ps.powerups[PW_BLUEFLAG] || self->client->ps.powerups[PW_NEUTRALFLAG] ) { // get the goal flag this player should have been going for if ( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTF_ELIMINATION) { if ( self->client->sess.sessionTeam == TEAM_BLUE ) { classname = "team_CTF_blueflag"; } else { classname = "team_CTF_redflag"; } } else { if ( self->client->sess.sessionTeam == TEAM_BLUE ) { classname = "team_CTF_redflag"; } else { classname = "team_CTF_blueflag"; } } ent = NULL; do { ent = G_Find(ent, FOFS(classname), classname); } while (ent && (ent->flags & FL_DROPPED_ITEM)); // if we found the destination flag and it's not picked up if (ent && !(ent->r.svFlags & SVF_NOCLIENT) ) { // if the player was *very* close VectorSubtract( self->client->ps.origin, ent->s.origin, dir ); if ( VectorLength(dir) < 200 ) { self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT; if ( attacker->client ) { attacker->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT; } } } } } /* ================== CheckAlmostScored ================== */ void CheckAlmostScored( gentity_t *self, gentity_t *attacker ) { gentity_t *ent; vec3_t dir; char *classname; // if the player was carrying cubes if ( self->client->ps.generic1 ) { if ( self->client->sess.sessionTeam == TEAM_BLUE ) { classname = "team_redobelisk"; } else { classname = "team_blueobelisk"; } ent = G_Find(NULL, FOFS(classname), classname); // if we found the destination obelisk if ( ent ) { // if the player was *very* close VectorSubtract( self->client->ps.origin, ent->s.origin, dir ); if ( VectorLength(dir) < 200 ) { self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT; if ( attacker->client ) { attacker->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT; } } } } } /* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i,counter2; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } //unlagged - backward reconciliation #2 // make sure the body shows up in the client's current position G_UnTimeShiftClient( self ); //unlagged - backward reconciliation #2 //KK-OAX Here is where we run the streak logic. G_RunStreakLogic( attacker, self ); // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = ""; } } else { killer = ENTITYNUM_WORLD; killerName = ""; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = ""; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = ""; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; //Sago: Hmmm... generic? Can I transmit anything I like? Like if it is a team kill? Let's try ent->s.generic1 = OnSameTeam (self, attacker); if( !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime) ) ent->r.svFlags = SVF_BROADCAST; // send to everyone (if not an elimination gametype during active warmup) else ent->r.svFlags = SVF_NOCLIENT; self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if( (g_gametype.integer client->ps.persistant[PERS_SCORE]>0) || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( attacker, self->r.currentOrigin, -1 ); } else { if(g_gametype.integer!=GT_LMS) AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // Attack gets a challenge complete: if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT)) ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 0, attacker->client->pers.netname, "GAUNTLET" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } //If neither attacker or taget is bots and not the same if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT) && self!=attacker) { switch(meansOfDeath) { case MOD_GAUNTLET: ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); break; case MOD_MACHINEGUN: ChallengeMessage(attacker,WEAPON_MACHINEGUN_KILLS); break; case MOD_SHOTGUN: ChallengeMessage(attacker,WEAPON_SHOTGUN_KILLS); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: ChallengeMessage(attacker,WEAPON_GRANADE_KILLS); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: ChallengeMessage(attacker,WEAPON_ROCKET_KILLS); break; case MOD_LIGHTNING: ChallengeMessage(attacker,WEAPON_LIGHTNING_KILLS); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: ChallengeMessage(attacker,WEAPON_PLASMA_KILLS); break; case MOD_RAILGUN: if(g_instantgib.integer) ChallengeMessage(attacker,WEAPON_INSTANT_RAIL_KILLS); else ChallengeMessage(attacker,WEAPON_RAIL_KILLS); break; case MOD_BFG: case MOD_BFG_SPLASH: ChallengeMessage(attacker,WEAPON_BFG_KILLS); break; case MOD_NAIL: ChallengeMessage(attacker,WEAPON_NAILGUN_KILLS); break; case MOD_CHAINGUN: ChallengeMessage(attacker,WEAPON_CHAINGUN_KILLS); break; case MOD_PROXIMITY_MINE: ChallengeMessage(attacker,WEAPON_MINE_KILLS); break; case MOD_GRAPPLE: ChallengeMessage(attacker,WEAPON_GRAPPLE_KILLS); break; case MOD_LAVA: case MOD_SLIME: case MOD_TRIGGER_HURT: case MOD_FALLING: ChallengeMessage(attacker,WEAPON_PUSH_KILLS); break; case MOD_CRUSH: ChallengeMessage(attacker,WEAPON_CRUSH_KILLS); break; case MOD_TELEFRAG: ChallengeMessage(attacker,WEAPON_TELEFRAG_KILLS); break; }; ChallengeMessage(attacker,GENERAL_TOTALKILLS); ChallengeMessage(self,GENERAL_TOTALDEATHS); //Lets count number of powerups: i = 0; counter2 = 0; if(attacker->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_QUAD_KILL); counter2++; } if(self->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_COUNTER_QUAD); i++; } if(attacker->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_SPEED_KILL); counter2++; } if(self->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_COUNTER_SPEED); i++; } if(attacker->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_INVIS_KILL); counter2++; } if(self->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_COUNTER_INVIS); i++; } if(attacker->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_FLIGHT_KILL); counter2++; } if(self->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_COUNTER_FLIGHT); i++; } if(self->client->ps.powerups[PW_BATTLESUIT]) { ChallengeMessage(attacker,POWERUP_COUNTER_ENVIR); i++; } if(self->client->ps.powerups[PW_REGEN]) { ChallengeMessage(attacker,POWERUP_COUNTER_REGEN); i++; } if(i>1) //The target had more than one powerup ChallengeMessage(attacker,POWERUP_COUNTER_MULTI); if(counter2>1) //The attacker has more than one powerup ChallengeMessage(attacker,POWERUP_MULTI_KILL); } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // KK-OAX // Check if Multikills are enabled if( g_altExcellent.integer ) { attacker->client->pers.multiKillCount++; G_checkForMultiKill( attacker ); } // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 1, attacker->client->pers.netname, "EXCELLENT" ); if(!level.hadBots) //There has not been any bots ChallengeMessage(attacker,AWARD_EXCELLENT); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } else { //KK-OAX Clear multikill count //Must be 1 so the correct number of kills are displayed to the clients. attacker->client->pers.multiKillCount = 1; } attacker->client->lastKillTime = level.time; } } else { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if(self->client->ps.persistant[PERS_SCORE]>0 || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } // if client is in a nodrop area, don't drop anything (but return CTF flags!) TossClientItems( self ); //#endif Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700 +i; if(g_respawntime.integer>0) { for(i=0; self->client->respawnTime > i*g_respawntime.integer*1000;i++); self->client->respawnTime = i*g_respawntime.integer*1000; } //For testing: //G_Printf("Respawntime: %i\n",self->client->respawnTime); //However during warm up, we should respawn quicker! if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer) self->client->respawnTime = level.time + rand()%800; RespawnTimeMessage(self,self->client->respawnTime); // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } } trap_LinkEntity (self); } /* ================ CheckArmor ================ */ int CheckArmor (gentity_t *ent, int damage, int dflags) { gclient_t *client; int save; int count; if (!damage) return 0; client = ent->client; if (!client) return 0; if (dflags & DAMAGE_NO_ARMOR) return 0; // armor count = client->ps.stats[STAT_ARMOR]; save = ceil( damage * ARMOR_PROTECTION ); if (save >= count) save = count; if (!save) return 0; client->ps.stats[STAT_ARMOR] -= save; return save; } /* ================ RaySphereIntersections ================ */ int RaySphereIntersections( vec3_t origin, float radius, vec3_t point, vec3_t dir, vec3_t intersections[2] ) { float b, c, d, t; // | origin - (point + t * dir) | = radius // a = dir[0]^2 + dir[1]^2 + dir[2]^2; // b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2])); // c = (point[0] - origin[0])^2 + (point[1] - origin[1])^2 + (point[2] - origin[2])^2 - radius^2; // normalize dir so a = 1 VectorNormalize(dir); b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2])); c = (point[0] - origin[0]) * (point[0] - origin[0]) + (point[1] - origin[1]) * (point[1] - origin[1]) + (point[2] - origin[2]) * (point[2] - origin[2]) - radius * radius; d = b * b - 4 * c; if (d > 0) { t = (- b + sqrt(d)) / 2; VectorMA(point, t, dir, intersections[0]); t = (- b - sqrt(d)) / 2; VectorMA(point, t, dir, intersections[1]); return 2; } else if (d == 0) { t = (- b ) / 2; VectorMA(point, t, dir, intersections[0]); return 1; } return 0; } /* ================ G_InvulnerabilityEffect ================ */ int G_InvulnerabilityEffect( gentity_t *targ, vec3_t dir, vec3_t point, vec3_t impactpoint, vec3_t bouncedir ) { gentity_t *impact; vec3_t intersections[2], vec; int n; if ( !targ->client ) { return qfalse; } VectorCopy(dir, vec); VectorInverse(vec); // sphere model radius = 42 units n = RaySphereIntersections( targ->client->ps.origin, 42, point, vec, intersections); if (n > 0) { impact = G_TempEntity( targ->client->ps.origin, EV_INVUL_IMPACT ); VectorSubtract(intersections[0], targ->client->ps.origin, vec); vectoangles(vec, impact->s.angles); impact->s.angles[0] += 90; if (impact->s.angles[0] > 360) impact->s.angles[0] -= 360; if ( impactpoint ) { VectorCopy( intersections[0], impactpoint ); } if ( bouncedir ) { VectorCopy( vec, bouncedir ); VectorNormalize( bouncedir ); } return qtrue; } else { return qfalse; } } /* catchup_damage */ static int catchup_damage(int damage, int attacker_points, int target_points) { int newdamage; if(g_catchup.integer <= 0 ) return damage; //Reduce damage if(attacker_points<=target_points+5) return damage; //Never reduce damage if only 5 points ahead. newdamage=damage-((attacker_points-target_points-5) * (g_catchup.integer*damage))/100; if(newdamagetakedamage) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } if ( targ->client && mod != MOD_JUICED) { if ( targ->client->invulnerabilityTime > level.time) { if ( dir && point ) { G_InvulnerabilityEffect( targ, dir, point, impactpoint, bouncedir ); } return; } } //Sago: This was moved up client = targ->client; //Sago: See if the client was sent flying //Check if damage is by somebody who is not a player! if( (!attacker || attacker->s.eType != ET_PLAYER) && client && client->lastSentFlying>-1 && ( mod==MOD_FALLING || mod==MOD_LAVA || mod==MOD_SLIME || mod==MOD_TRIGGER_HURT || mod==MOD_SUICIDE) ) { if( client->lastSentFlyingTime+5000lastSentFlying = -1; //More than 5 seconds, not a kill! } else { //G_Printf("LastSentFlying %i\n",client->lastSentFlying); attacker = &g_entities[client->lastSentFlying]; } } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } if( g_gametype.integer == GT_OBELISK && CheckObeliskAttack( targ, attacker ) ) { return; } // reduce damage by the attacker's handicap value // unless they are rocket jumping if ( attacker->client && attacker != targ ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max /= 2; } damage = damage * max / 100; } //Sago: I have moved this up //client = targ->client; if ( client ) { if ( client->noclip ) { return; } } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } //Remeber the last person to hurt the player if( !g_awardpushing.integer || targ==attacker || OnSameTeam (targ, attacker)) { targ->client->lastSentFlying = -1; } else { /*if ( pm->waterlevel <= 1 ) { if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { // if getting knocked back, no friction if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*pm_friction*pml.frametime; } } }*/ targ->client->lastSentFlying = attacker->s.number; targ->client->lastSentFlyingTime = level.time; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( mod != MOD_JUICED && mod != MOD_CRUSH && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam (targ, attacker) ) { if ( ( !g_friendlyFire.integer && g_gametype.integer != GT_ELIMINATION && g_gametype.integer != GT_CTF_ELIMINATION ) || ( g_elimination_selfdamage.integer<2 && (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) ) ) { return; } } if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if(targ->client && targ->client->spawnprotected) { if(level.time>targ->client->respawnTime+g_spawnprotect.integer) targ->client->spawnprotected = qfalse; else if( (mod > MOD_UNKNOWN && mod < MOD_WATER) || mod == MOD_TELEFRAG || mod>MOD_TRIGGER_HURT) return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if(targ && targ->client && attacker->client ) damage = catchup_damage(damage, attacker->client->ps.persistant[PERS_SCORE], targ->client->ps.persistant[PERS_SCORE]); if(g_damageModifier.value > 0.01) { damage *= g_damageModifier.value; } if ( damage < 1 ) { damage = 1; } if(targ == attacker && (g_dmflags.integer & DF_NO_SELF_DAMAGE) ) damage = 0; if ((g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS || g_elimination_allgametypes.integer) && g_elimination_selfdamage.integer<1 && ( targ == attacker || mod == MOD_FALLING )) { damage = 0; } //So people can be telefragged! if ((g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) && level.time < level.roundStartTime && ((mod == MOD_LAVA) || (mod == MOD_SLIME)) ) { damage = 1000; } take = damage; //save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else if(client->lastSentFlying) { client->ps.persistant[PERS_ATTACKER] = client->lastSentFlying; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF || g_gametype.integer == GT_CTF_ELIMINATION) { Team_CheckHurtCarrier(targ, attacker); } if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } //If vampire is enabled, gain health but not from self or teammate, cannot steal more than targ has if( g_vampire.value>0.0 && (targ != attacker) && take > 0 && !(OnSameTeam(targ, attacker)) && attacker->health > 0 && targ->health > 0 ) { if(takehealth) attacker->health += (int)(((float)take)*g_vampire.value); else attacker->health += (int)(((float)targ->health)*g_vampire.value); if(attacker->health>g_vampireMaxHealth.integer) attacker->health = g_vampireMaxHealth.integer; } // do the damage if (take) { targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } } /* ============ CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean CanDamage (gentity_t *targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; // use the midpoint of the bounds instead of the origin, because // bmodels may have their origin is 0,0,0 VectorAdd (targ->r.absmin, targ->r.absmax, midpoint); VectorScale (midpoint, 0.5, midpoint); VectorCopy (midpoint, dest); trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0 || tr.entityNum == targ->s.number) return qtrue; // this should probably check in the plane of projection, // rather than in world coordinate, and also include Z VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; return qfalse; } /* ============ G_RadiusDamage ============ */ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if (ent == ignore) continue; if (!ent->takedamage) continue; // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if( CanDamage (ent, origin) ) { if( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod); } } return hitClient; } openarena_0.8.8.orig/code/game/g_team.h0000644000175000017500000001401111656310264016453 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #ifdef MISSIONPACK #define CTF_CAPTURE_BONUS 100 // what you get for capture #define CTF_TEAM_BONUS 25 // what your team gets for capture #define CTF_RECOVERY_BONUS 10 // what you get for recovery #define CTF_FLAG_BONUS 10 // what you get for picking up enemy flag #define CTF_FRAG_CARRIER_BONUS 20 // what you get for fragging enemy flag carrier #define CTF_FLAG_RETURN_TIME 40000 // seconds until auto return #define CTF_CARRIER_DANGER_PROTECT_BONUS 5 // bonus for fraggin someone who has recently hurt your flag carrier #define CTF_CARRIER_PROTECT_BONUS 2 // bonus for fraggin someone while either you or your target are near your flag carrier #define CTF_FLAG_DEFENSE_BONUS 10 // bonus for fraggin someone while either you or your target are near your flag #define CTF_RETURN_FLAG_ASSIST_BONUS 10 // awarded for returning a flag that causes a capture to happen almost immediately #define CTF_FRAG_CARRIER_ASSIST_BONUS 10 // award for fragging a flag carrier if a capture happens almost immediately #else #define CTF_CAPTURE_BONUS 5 // what you get for capture #define CTF_TEAM_BONUS 0 // what your team gets for capture #define CTF_RECOVERY_BONUS 1 // what you get for recovery #define CTF_FLAG_BONUS 0 // what you get for picking up enemy flag #define CTF_FRAG_CARRIER_BONUS 2 // what you get for fragging enemy flag carrier #define CTF_FLAG_RETURN_TIME 40000 // seconds until auto return #define CTF_CARRIER_DANGER_PROTECT_BONUS 2 // bonus for fraggin someone who has recently hurt your flag carrier #define CTF_CARRIER_PROTECT_BONUS 1 // bonus for fraggin someone while either you or your target are near your flag carrier #define CTF_FLAG_DEFENSE_BONUS 1 // bonus for fraggin someone while either you or your target are near your flag #define CTF_RETURN_FLAG_ASSIST_BONUS 1 // awarded for returning a flag that causes a capture to happen almost immediately #define CTF_FRAG_CARRIER_ASSIST_BONUS 2 // award for fragging a flag carrier if a capture happens almost immediately #endif #ifdef MISSIONPACK //For Double Domination: #define DD_POINT_DEFENCE_BONUS 10 //Score for fragging someone while either you or target are near a Domination Point #define DD_POINT_DEFENCE_CLOSE_BONUS 25 //Score for fragging someone while either you or target are near a Domination Point and have almost scored //Following is added togehter: #define DD_POINT_CAPTURE 5 //Score for taking a point #define DD_POINT_CAPTURE_BREAK 10 //If the enemy was dominating #define DD_POINT_CAPTURE_CLOSE 15 //Extra score if the enemy was about to score #define DD_AT_POINT_AT_CAPTURE 30 //You was close to a point as capture succeded. #else //For Double Domination: #define DD_POINT_DEFENCE_BONUS 1 //Score for fragging someone while either you or target are near a Domination Point #define DD_POINT_DEFENCE_CLOSE_BONUS 2 //Score for fragging someone while either you or target are near a Domination Point and have almost scored //Following is added togehter: #define DD_POINT_CAPTURE 1 //Score for taking a point #define DD_POINT_CAPTURE_BREAK 1 //If the enemy was dominating #define DD_POINT_CAPTURE_CLOSE 1 //Extra score if the enemy was about to score #define DD_AT_POINT_AT_CAPTURE 1 //You was close to a point as capture succeded. #endif #define DD_CLOSE 3 //How many seconds to score is close #define CTF_TARGET_PROTECT_RADIUS 1000 // the radius around an object being defended where a target will be worth extra frags #define CTF_ATTACKER_PROTECT_RADIUS 1000 // the radius around an object being defended where an attacker will get extra frags when making kills #define CTF_CARRIER_DANGER_PROTECT_TIMEOUT 8000 #define CTF_FRAG_CARRIER_ASSIST_TIMEOUT 10000 #define CTF_RETURN_FLAG_ASSIST_TIMEOUT 10000 #define CTF_GRAPPLE_SPEED 750 // speed of grapple in flight #define CTF_GRAPPLE_PULL_SPEED 750 // speed player is pulled at #define OVERLOAD_ATTACK_BASE_SOUND_TIME 20000 // Prototypes int OtherTeam(int team); const char *TeamName(int team); const char *OtherTeamName(int team); const char *TeamColorString(int team); void AddTeamScore(vec3_t origin, int team, int score); void Team_DroppedFlagThink(gentity_t *ent); void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker); void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker); void Team_InitGame(void); void Team_ReturnFlag(int team); void Team_FreeEntity(gentity_t *ent); gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ); //For Double_D gentity_t *SelectDoubleDominationSpawnPoint ( team_t, vec3_t origin, vec3_t angles ); //For Standard D void Team_Dom_SpawnPoints( void ); gentity_t *Team_GetLocation(gentity_t *ent); qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen); void TeamplayInfoMessage( gentity_t *ent ); void CheckTeamStatus(void); int Pickup_Team( gentity_t *ent, gentity_t *other ); //Double Domination: int Team_SpawnDoubleDominationPoints ( void ); int Team_RemoveDoubleDominationPoints ( void ); void Team_DD_bonusAtPoints(int team); //Added to make gcc happy (and because I use it in main) void Team_ForceGesture(int team); openarena_0.8.8.orig/code/game/g_unlagged.c0000644000175000017500000004215611656310264017321 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2006 Neil Toronto. This file is part of the Unlagged source code. Unlagged source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Unlagged source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Unlagged source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //Sago: For some reason the Niels version must use a different char set. #include "g_local.h" //#include "g_local.h" /* ============ G_ResetHistory Clear out the given client's history (should be called when the teleport bit is flipped) ============ */ void G_ResetHistory( gentity_t *ent ) { int i, time; // fill up the history with data (assume the current position) ent->client->historyHead = NUM_CLIENT_HISTORY - 1; for ( i = ent->client->historyHead, time = level.time; i >= 0; i--, time -= 50 ) { VectorCopy( ent->r.mins, ent->client->history[i].mins ); VectorCopy( ent->r.maxs, ent->client->history[i].maxs ); VectorCopy( ent->r.currentOrigin, ent->client->history[i].currentOrigin ); ent->client->history[i].leveltime = time; } } /* ============ G_StoreHistory Keep track of where the client's been ============ */ void G_StoreHistory( gentity_t *ent ) { int head, frametime; frametime = level.time - level.previousTime; ent->client->historyHead++; if ( ent->client->historyHead >= NUM_CLIENT_HISTORY ) { ent->client->historyHead = 0; } head = ent->client->historyHead; // store all the collision-detection info and the time VectorCopy( ent->r.mins, ent->client->history[head].mins ); VectorCopy( ent->r.maxs, ent->client->history[head].maxs ); VectorCopy( ent->s.pos.trBase, ent->client->history[head].currentOrigin ); SnapVector( ent->client->history[head].currentOrigin ); ent->client->history[head].leveltime = level.time; } /* ============= TimeShiftLerp Used below to interpolate between two previous vectors Returns a vector "frac" times the distance between "start" and "end" ============= */ static void TimeShiftLerp( float frac, vec3_t start, vec3_t end, vec3_t result ) { // From CG_InterpolateEntityPosition in cg_ents.c: /* cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); */ // Making these exactly the same should avoid floating-point error result[0] = start[0] + frac * ( end[0] - start[0] ); result[1] = start[1] + frac * ( end[1] - start[1] ); result[2] = start[2] + frac * ( end[2] - start[2] ); } /* ================= G_TimeShiftClient Move a client back to where he was at the specified "time" ================= */ void G_TimeShiftClient( gentity_t *ent, int time, qboolean debug, gentity_t *debugger ) { int j, k; //char msg[2048]; // this will dump out the head index, and the time for all the stored positions /* if ( debug ) { char str[MAX_STRING_CHARS]; Com_sprintf(str, sizeof(str), "print \"head: %d, %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n\"", ent->client->historyHead, ent->client->history[0].leveltime, ent->client->history[1].leveltime, ent->client->history[2].leveltime, ent->client->history[3].leveltime, ent->client->history[4].leveltime, ent->client->history[5].leveltime, ent->client->history[6].leveltime, ent->client->history[7].leveltime, ent->client->history[8].leveltime, ent->client->history[9].leveltime, ent->client->history[10].leveltime, ent->client->history[11].leveltime, ent->client->history[12].leveltime, ent->client->history[13].leveltime, ent->client->history[14].leveltime, ent->client->history[15].leveltime, ent->client->history[16].leveltime); trap_SendServerCommand( debugger - g_entities, str ); } */ // find two entries in the history whose times sandwich "time" // assumes no two adjacent records have the same timestamp j = k = ent->client->historyHead; do { if ( ent->client->history[j].leveltime <= time ) break; k = j; j--; if ( j < 0 ) { j = NUM_CLIENT_HISTORY - 1; } } while ( j != ent->client->historyHead ); // if we got past the first iteration above, we've sandwiched (or wrapped) if ( j != k ) { // make sure it doesn't get re-saved if ( ent->client->saved.leveltime != level.time ) { // save the current origin and bounding box VectorCopy( ent->r.mins, ent->client->saved.mins ); VectorCopy( ent->r.maxs, ent->client->saved.maxs ); VectorCopy( ent->r.currentOrigin, ent->client->saved.currentOrigin ); ent->client->saved.leveltime = level.time; } // if we haven't wrapped back to the head, we've sandwiched, so // we shift the client's position back to where he was at "time" if ( j != ent->client->historyHead ) { float frac = (float)(time - ent->client->history[j].leveltime) / (float)(ent->client->history[k].leveltime - ent->client->history[j].leveltime); // interpolate between the two origins to give position at time index "time" TimeShiftLerp( frac, ent->client->history[j].currentOrigin, ent->client->history[k].currentOrigin, ent->r.currentOrigin ); // lerp these too, just for fun (and ducking) TimeShiftLerp( frac, ent->client->history[j].mins, ent->client->history[k].mins, ent->r.mins ); TimeShiftLerp( frac, ent->client->history[j].maxs, ent->client->history[k].maxs, ent->r.maxs ); /*if ( debug && debugger != NULL ) { // print some debugging stuff exactly like what the client does // it starts with "Rec:" to let you know it backward-reconciled Com_sprintf( msg, sizeof(msg), "print \"^1Rec: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n" "^2frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n" "^7level.time: %d, est time: %d, level.time delta: %d, est real ping: %d\n\"", time, ent->client->history[j].leveltime, ent->client->history[k].leveltime, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], frac, ent->client->history[j].currentOrigin[0], ent->client->history[j].currentOrigin[1], ent->client->history[j].currentOrigin[2], ent->client->history[k].currentOrigin[0], ent->client->history[k].currentOrigin[1], ent->client->history[k].currentOrigin[2], level.time, level.time + debugger->client->frameOffset, level.time - time, level.time + debugger->client->frameOffset - time); trap_SendServerCommand( debugger - g_entities, msg ); }*/ // this will recalculate absmin and absmax trap_LinkEntity( ent ); } else { // we wrapped, so grab the earliest VectorCopy( ent->client->history[k].currentOrigin, ent->r.currentOrigin ); VectorCopy( ent->client->history[k].mins, ent->r.mins ); VectorCopy( ent->client->history[k].maxs, ent->r.maxs ); // this will recalculate absmin and absmax trap_LinkEntity( ent ); } } else { // this only happens when the client is using a negative timenudge, because that // number is added to the command time // print some debugging stuff exactly like what the client does // it starts with "No rec:" to let you know it didn't backward-reconcile //Sago: This code looks wierd } } /* ===================== G_TimeShiftAllClients Move ALL clients back to where they were at the specified "time", except for "skip" ===================== */ void G_TimeShiftAllClients( int time, gentity_t *skip ) { int i; gentity_t *ent; qboolean debug = ( skip != NULL && skip->client && /*skip->client->pers.debugDelag && */ skip->s.weapon == WP_RAILGUN ); // for every client ent = &g_entities[0]; for ( i = 0; i < MAX_CLIENTS; i++, ent++ ) { if ( ent->client && ent->inuse && ent->client->sess.sessionTeam < TEAM_SPECTATOR && ent != skip ) { G_TimeShiftClient( ent, time, debug, skip ); } } } /* ================ G_DoTimeShiftFor Decide what time to shift everyone back to, and do it ================ */ void G_DoTimeShiftFor( gentity_t *ent ) { int wpflags[WP_NUM_WEAPONS] = { 0, 0, 2, 4, 0, 0, 8, 16, 0, 0, 0, 32, 0, 64 }; int wpflag = wpflags[ent->client->ps.weapon]; int time; // don't time shift for mistakes or bots if ( !ent->inuse || !ent->client || (ent->r.svFlags & SVF_BOT) ) { return; } // if it's enabled server-side and the client wants it or wants it for this weapon if ( g_delagHitscan.integer && ( ent->client->pers.delag & 1 || ent->client->pers.delag & wpflag ) ) { // do the full lag compensation, except what the client nudges time = ent->client->attackTime + ent->client->pers.cmdTimeNudge; //Give the lightning gun some handicap (lag was part of weapon balance in VQ3) if(ent->client->ps.weapon == WP_LIGHTNING && g_lagLightning.integer) time+=50; } else { // do just 50ms time = level.previousTime + ent->client->frameOffset; } G_TimeShiftAllClients( time, ent ); } /* =================== G_UnTimeShiftClient Move a client back to where he was before the time shift =================== */ void G_UnTimeShiftClient( gentity_t *ent ) { // if it was saved if ( ent->client->saved.leveltime == level.time ) { // move it back VectorCopy( ent->client->saved.mins, ent->r.mins ); VectorCopy( ent->client->saved.maxs, ent->r.maxs ); VectorCopy( ent->client->saved.currentOrigin, ent->r.currentOrigin ); ent->client->saved.leveltime = 0; // this will recalculate absmin and absmax trap_LinkEntity( ent ); } } /* ======================= G_UnTimeShiftAllClients Move ALL the clients back to where they were before the time shift, except for "skip" ======================= */ void G_UnTimeShiftAllClients( gentity_t *skip ) { int i; gentity_t *ent; ent = &g_entities[0]; for ( i = 0; i < MAX_CLIENTS; i++, ent++) { if ( ent->client && ent->inuse && ent->client->sess.sessionTeam < TEAM_SPECTATOR && ent != skip ) { G_UnTimeShiftClient( ent ); } } } /* ================== G_UndoTimeShiftFor Put everyone except for this client back where they were ================== */ void G_UndoTimeShiftFor( gentity_t *ent ) { // don't un-time shift for mistakes or bots if ( !ent->inuse || !ent->client || (ent->r.svFlags & SVF_BOT) ) { return; } G_UnTimeShiftAllClients( ent ); } /* =========================== G_PredictPlayerClipVelocity Slide on the impacting surface =========================== */ #define OVERCLIP 1.001f void G_PredictPlayerClipVelocity( vec3_t in, vec3_t normal, vec3_t out ) { float backoff; // find the magnitude of the vector "in" along "normal" backoff = DotProduct (in, normal); // tilt the plane a bit to avoid floating-point error issues if ( backoff < 0 ) { backoff *= OVERCLIP; } else { backoff /= OVERCLIP; } // slide along VectorMA( in, -backoff, normal, out ); } /* ======================== G_PredictPlayerSlideMove Advance the given entity frametime seconds, sliding as appropriate ======================== */ #define MAX_CLIP_PLANES 5 qboolean G_PredictPlayerSlideMove( gentity_t *ent, float frametime ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, velocity, origin; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; // vec3_t worldUp = { 0.0f, 0.0f, 1.0f }; numbumps = 4; VectorCopy( ent->s.pos.trDelta, primal_velocity ); VectorCopy( primal_velocity, velocity ); VectorCopy( ent->s.pos.trBase, origin ); VectorCopy( velocity, endVelocity ); time_left = frametime; numplanes = 0; for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) { // calculate position we are trying to move to VectorMA( origin, time_left, velocity, end ); // see if we can make it there trap_Trace( &trace, origin, ent->r.mins, ent->r.maxs, end, ent->s.number, ent->clipmask ); if (trace.allsolid) { // entity is completely trapped in another solid VectorClear( velocity ); VectorCopy( origin, ent->s.pos.trBase ); return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy( trace.endpos, origin ); } if (trace.fraction == 1) { break; // moved the entire distance } time_left -= time_left * trace.fraction; if ( numplanes >= MAX_CLIP_PLANES ) { // this shouldn't really happen VectorClear( velocity ); VectorCopy( origin, ent->s.pos.trBase ); return qtrue; } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // for ( i = 0; i < numplanes; i++ ) { if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) { VectorAdd( trace.plane.normal, velocity, velocity ); break; } } if ( i < numplanes ) { continue; } VectorCopy( trace.plane.normal, planes[numplanes] ); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0; i < numplanes; i++ ) { into = DotProduct( velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // slide along the plane G_PredictPlayerClipVelocity( velocity, planes[i], clipVelocity ); // slide along the plane G_PredictPlayerClipVelocity( endVelocity, planes[i], endClipVelocity ); // see if there is a second plane that the new move enters for ( j = 0; j < numplanes; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane G_PredictPlayerClipVelocity( clipVelocity, planes[j], clipVelocity ); G_PredictPlayerClipVelocity( endClipVelocity, planes[j], endClipVelocity ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct( planes[i], planes[j], dir ); VectorNormalize( dir ); d = DotProduct( dir, velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct( planes[i], planes[j], dir ); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0; k < numplanes; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( velocity ); VectorCopy( origin, ent->s.pos.trBase ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } VectorCopy( endVelocity, velocity ); VectorCopy( origin, ent->s.pos.trBase ); return (bumpcount != 0); } /* ============================ G_PredictPlayerStepSlideMove Advance the given entity frametime seconds, stepping and sliding as appropriate ============================ */ #define STEPSIZE 18 void G_PredictPlayerStepSlideMove( gentity_t *ent, float frametime ) { vec3_t start_o, start_v, down_o, down_v; vec3_t down, up; trace_t trace; float stepSize; VectorCopy (ent->s.pos.trBase, start_o); VectorCopy (ent->s.pos.trDelta, start_v); if ( !G_PredictPlayerSlideMove( ent, frametime ) ) { // not clipped, so forget stepping return; } VectorCopy( ent->s.pos.trBase, down_o); VectorCopy( ent->s.pos.trDelta, down_v); VectorCopy (start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher trap_Trace( &trace, start_o, ent->r.mins, ent->r.maxs, up, ent->s.number, ent->clipmask ); if ( trace.allsolid ) { return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy( trace.endpos, ent->s.pos.trBase ); VectorCopy( start_v, ent->s.pos.trDelta ); G_PredictPlayerSlideMove( ent, frametime ); // push down the final amount VectorCopy( ent->s.pos.trBase, down ); down[2] -= stepSize; trap_Trace( &trace, ent->s.pos.trBase, ent->r.mins, ent->r.maxs, down, ent->s.number, ent->clipmask ); if ( !trace.allsolid ) { VectorCopy( trace.endpos, ent->s.pos.trBase ); } if ( trace.fraction < 1.0 ) { G_PredictPlayerClipVelocity( ent->s.pos.trDelta, trace.plane.normal, ent->s.pos.trDelta ); } } /* =================== G_PredictPlayerMove Advance the given entity frametime seconds, stepping and sliding as appropriate This is the entry point to the server-side-only prediction code =================== */ void G_PredictPlayerMove( gentity_t *ent, float frametime ) { G_PredictPlayerStepSlideMove( ent, frametime ); } openarena_0.8.8.orig/code/game/g_session.c0000644000175000017500000001125611656310264017213 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" /* ======================================================================= SESSION DATA Session data is the only data that stays persistant across level loads and tournament restarts. ======================================================================= */ /* ================ G_WriteClientSessionData Called on game shutdown ================ */ void G_WriteClientSessionData( gclient_t *client ) { const char *s; const char *var; s = va("%i %i %i %i %i %i %i", client->sess.sessionTeam, client->sess.spectatorNum, client->sess.spectatorState, client->sess.spectatorClient, client->sess.wins, client->sess.losses, client->sess.teamLeader ); var = va( "session%i", (int)(client - level.clients) ); trap_Cvar_Set( var, s ); } /* ================ G_ReadSessionData Called on a reconnect ================ */ void G_ReadSessionData( gclient_t *client ) { char s[MAX_STRING_CHARS]; const char *var; // bk001205 - format int teamLeader; int spectatorState; int sessionTeam; var = va( "session%i", (int)(client - level.clients) ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); sscanf( s, "%i %i %i %i %i %i %i", &sessionTeam, // bk010221 - format &client->sess.spectatorNum, &spectatorState, // bk010221 - format &client->sess.spectatorClient, &client->sess.wins, &client->sess.losses, &teamLeader // bk010221 - format ); // bk001205 - format issues client->sess.sessionTeam = (team_t)sessionTeam; client->sess.spectatorState = (spectatorState_t)spectatorState; client->sess.teamLeader = (qboolean)teamLeader; } /* ================ G_InitSessionData Called on a first-time connect ================ */ void G_InitSessionData( gclient_t *client, char *userinfo ) { clientSession_t *sess; const char *value; sess = &client->sess; // initial team determination if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { if ( g_teamAutoJoin.integer ) { sess->sessionTeam = PickTeam( -1 ); BroadcastTeamChange( client, -1 ); } else { // always spawn as spectator in team games sess->sessionTeam = TEAM_SPECTATOR; } } else { value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 's' ) { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; } else { switch ( g_gametype.integer ) { default: case GT_FFA: case GT_LMS: case GT_SINGLE_PLAYER: if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; case GT_TOURNAMENT: // if the game is full, go into a waiting mode if ( level.numNonSpectatorClients >= 2 ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; } } } sess->spectatorState = SPECTATOR_FREE; AddTournamentQueue(client); G_WriteClientSessionData( client ); } /* ================== G_InitWorldSession ================== */ void G_InitWorldSession( void ) { char s[MAX_STRING_CHARS]; int gt; trap_Cvar_VariableStringBuffer( "session", s, sizeof(s) ); gt = atoi( s ); // if the gametype changed since the last session, don't use any // client sessions if ( g_gametype.integer != gt ) { level.newSession = qtrue; G_Printf( "Gametype changed, clearing session data.\n" ); } } /* ================== G_WriteSessionData ================== */ void G_WriteSessionData( void ) { int i; trap_Cvar_Set( "session", va("%i", g_gametype.integer) ); for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_CONNECTED ) { G_WriteClientSessionData( &level.clients[i] ); } } } openarena_0.8.8.orig/code/game/challenges.h0000644000175000017500000000712711656310264017336 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2008-2009 Poul Sander This file is part of Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /** * This is a list of challenges that a player can only complete while playing * against human opponents, either LAN or Internet. * * Hopefully this will make it a little more fun to play online even if you * are not good enough to win you can still track your progress. */ #define CHALLENGES_MAX_COUNT MAX_INT-1 #define CHALLENGES_MAX 2048 #define GENERAL_TEST 0 #define GENERAL_TOTALKILLS 1 #define GENERAL_TOTALDEATHS 2 #define GENERAL_TOTALGAMES 3 //Gametypes #define GAMETYPES_FFA_WINS 101 #define GAMETYPES_TOURNEY_WINS 102 #define GAMETYPES_TDM_WINS 103 #define GAMETYPES_CTF_WINS 104 #define GAMETYPES_1FCTF_WINS 105 #define GAMETYPES_OVERLOAD_WINS 106 #define GAMETYPES_HARVESTER_WINS 107 #define GAMETYPES_ELIMINATION_WINS 108 #define GAMETYPES_CTF_ELIMINATION_WINS 109 #define GAMETYPES_LMS_WINS 110 #define GAMETYPES_DD_WINS 111 #define GAMETYPES_DOM_WINS 112 //Weapons #define WEAPON_GAUNTLET_KILLS 201 #define WEAPON_MACHINEGUN_KILLS 202 #define WEAPON_SHOTGUN_KILLS 203 #define WEAPON_GRANADE_KILLS 204 #define WEAPON_ROCKET_KILLS 205 #define WEAPON_LIGHTNING_KILLS 206 #define WEAPON_PLASMA_KILLS 207 #define WEAPON_RAIL_KILLS 208 #define WEAPON_BFG_KILLS 209 #define WEAPON_GRAPPLE_KILLS 210 #define WEAPON_CHAINGUN_KILLS 211 #define WEAPON_NAILGUN_KILLS 212 #define WEAPON_MINE_KILLS 213 #define WEAPON_PUSH_KILLS 214 #define WEAPON_INSTANT_RAIL_KILLS 215 #define WEAPON_TELEFRAG_KILLS 216 #define WEAPON_CRUSH_KILLS 217 //Awards //Gauntlet is not here as it is the same as WEAPON_GAUNTLET_KILLS #define AWARD_IMPRESSIVE 301 #define AWARD_EXCELLENT 302 #define AWARD_CAPTURE 303 #define AWARD_ASSIST 304 #define AWARD_DEFENCE 305 //Powerups #define POWERUP_QUAD_KILL 401 #define POWERUP_SPEED_KILL 402 #define POWERUP_FLIGHT_KILL 403 #define POWERUP_INVIS_KILL 404 #define POWERUP_MULTI_KILL 405 #define POWERUP_COUNTER_QUAD 406 #define POWERUP_COUNTER_SPEED 407 #define POWERUP_COUNTER_FLIGHT 408 #define POWERUP_COUNTER_INVIS 409 #define POWERUP_COUNTER_ENVIR 410 #define POWERUP_COUNTER_REGEN 411 #define POWERUP_COUNTER_MULTI 412 //FFA awards #define FFA_TOP3 501 //From behind, enemy gets fraglimit-1, player has at most fraglimit-2 but wins anyway #define FFA_FROMBEHIND 502 //BetterThan: loose a match but have a positive kill ratio against the winner #define FFA_BETTERTHAN 503 //Get at least half of your kills for players in the best half of the scoreboard #define FFA_JUDGE 504 //The oppesite #define FFA_CHEAPKILLER 505 openarena_0.8.8.orig/code/game/g_team.c0000644000175000017500000020160211656310264016452 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Open Arena source code. Portions copied from Tremulous under GPL version 2 including any later version. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" typedef struct teamgame_s { float last_flag_capture; int last_capture_team; flagStatus_t redStatus; // CTF flagStatus_t blueStatus; // CTF flagStatus_t flagStatus; // One Flag CTF int redTakenTime; int blueTakenTime; int redObeliskAttackedTime; int blueObeliskAttackedTime; } teamgame_t; teamgame_t teamgame; gentity_t *neutralObelisk; //Some pointers for Double Domination so we don't need GFind (I think it might crash at random times...) gentity_t *ddA; gentity_t *ddB; //Pointers for Standard Domination gentity_t *dom_points[MAX_DOMINATION_POINTS]; void Team_SetFlagStatus( int team, flagStatus_t status ); qboolean dominationPointsSpawned; void Team_InitGame( void ) { memset(&teamgame, 0, sizeof teamgame); switch( g_gametype.integer ) { case GT_CTF: case GT_CTF_ELIMINATION: case GT_DOUBLE_D: teamgame.redStatus = -1; // Invalid to force update Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE ); teamgame.blueStatus = -1; // Invalid to force update Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE ); ddA = NULL; ddB = NULL; break; case GT_DOMINATION: dominationPointsSpawned = qfalse; break; case GT_1FCTF: teamgame.flagStatus = -1; // Invalid to force update Team_SetFlagStatus( TEAM_FREE, FLAG_ATBASE ); break; default: break; } } int OtherTeam(int team) { if (team==TEAM_RED) return TEAM_BLUE; else if (team==TEAM_BLUE) return TEAM_RED; return team; } const char *TeamName(int team) { if (team==TEAM_RED) return "RED"; else if (team==TEAM_BLUE) return "BLUE"; else if (team==TEAM_SPECTATOR) return "SPECTATOR"; return "FREE"; } const char *OtherTeamName(int team) { if (team==TEAM_RED) return "BLUE"; else if (team==TEAM_BLUE) return "RED"; else if (team==TEAM_SPECTATOR) return "SPECTATOR"; return "FREE"; } const char *TeamColorString(int team) { if (team==TEAM_RED) return S_COLOR_RED; else if (team==TEAM_BLUE) return S_COLOR_BLUE; else if (team==TEAM_SPECTATOR) return S_COLOR_YELLOW; return S_COLOR_WHITE; } // NULL for everyone void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) { char msg[1024]; va_list argptr; char *p; va_start (argptr,fmt); if (Q_vsnprintf (msg, sizeof(msg), fmt, argptr) >= sizeof(msg)) { G_Error ( "PrintMsg overrun" ); } va_end (argptr); // double quotes are bad while ((p = strchr(msg, '"')) != NULL) *p = '\''; trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg )); } /* ================ KK-OAX From Tremulous G_TeamFromString Return the team referenced by a string ================ */ team_t G_TeamFromString( char *str ) { switch( tolower( *str ) ) { case '0': case 's': return TEAM_NONE; case '1': case 'f': return TEAM_FREE; case '2': case 'r': return TEAM_RED; case '3': case 'b': return TEAM_BLUE; default: return TEAM_NUM_TEAMS; } } /* ============== AddTeamScore used for gametype > GT_TEAM for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c ============== */ void AddTeamScore(vec3_t origin, int team, int score) { gentity_t *te; if ( g_gametype.integer != GT_DOMINATION ) { te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND ); te->r.svFlags |= SVF_BROADCAST; if ( team == TEAM_RED ) { if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) { //teams are tied sound te->s.eventParm = GTS_TEAMS_ARE_TIED; } else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] && level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) { // red took the lead sound te->s.eventParm = GTS_REDTEAM_TOOK_LEAD; } else { // red scored sound te->s.eventParm = GTS_REDTEAM_SCORED; } } else { if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) { //teams are tied sound te->s.eventParm = GTS_TEAMS_ARE_TIED; } else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] && level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) { // blue took the lead sound te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD; } else { // blue scored sound te->s.eventParm = GTS_BLUETEAM_SCORED; } } } level.teamScores[ team ] += score; } /* ============== OnSameTeam ============== */ qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) { if ( !ent1->client || !ent2->client ) { return qfalse; } if ( g_gametype.integer < GT_TEAM || g_ffa_gt==1) { return qfalse; } if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) { return qtrue; } return qfalse; } static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' }; static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' }; void Team_SetFlagStatus( int team, flagStatus_t status ) { qboolean modified = qfalse; switch( team ) { case TEAM_RED: // CTF if( teamgame.redStatus != status ) { teamgame.redStatus = status; modified = qtrue; } break; case TEAM_BLUE: // CTF if( teamgame.blueStatus != status ) { teamgame.blueStatus = status; modified = qtrue; } break; case TEAM_FREE: // One Flag CTF if( teamgame.flagStatus != status ) { teamgame.flagStatus = status; modified = qtrue; } break; } if( modified ) { char st[4]; if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTF_ELIMINATION) { st[0] = ctfFlagStatusRemap[teamgame.redStatus]; st[1] = ctfFlagStatusRemap[teamgame.blueStatus]; st[2] = 0; } else if (g_gametype.integer == GT_DOUBLE_D) { st[0] = oneFlagStatusRemap[teamgame.redStatus]; st[1] = oneFlagStatusRemap[teamgame.blueStatus]; st[2] = 0; } else { // GT_1FCTF st[0] = oneFlagStatusRemap[teamgame.flagStatus]; st[1] = 0; } trap_SetConfigstring( CS_FLAGSTATUS, st ); } } void Team_CheckDroppedItem( gentity_t *dropped ) { if( dropped->item->giTag == PW_REDFLAG ) { Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED ); } else if( dropped->item->giTag == PW_BLUEFLAG ) { Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED ); } else if( dropped->item->giTag == PW_NEUTRALFLAG ) { Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED ); } } /* ================ Team_ForceGesture ================ */ void Team_ForceGesture(int team) { int i; gentity_t *ent; for (i = 0; i < MAX_CLIENTS; i++) { ent = &g_entities[i]; if (!ent->inuse) continue; if (!ent->client) continue; if (ent->client->sess.sessionTeam != team) continue; ent->flags |= FL_FORCE_GESTURE; } } /* ================ Team_DD_bonusAtPoints Adds bonus point to a player if he is close to the point and on the team that scores ================ */ void Team_DD_bonusAtPoints(int team) { vec3_t v1, v2; int i; gentity_t *player; for (i = 0; i < MAX_CLIENTS; i++) { player = &g_entities[i]; if (!player->inuse) continue; if (!player->client) continue; if( player->client->sess.sessionTeam != team ) return; //player was not on scoring team //See if the player is close to any of the points: VectorSubtract(player->r.currentOrigin, ddA->r.currentOrigin, v1); VectorSubtract(player->r.currentOrigin, ddB->r.currentOrigin, v2); if (!( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddA->r.currentOrigin, player->r.currentOrigin ) ) || ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddB->r.currentOrigin, player->r.currentOrigin ) ) ))) return; //Wasn't close to any of the points AddScore(player, player->r.currentOrigin, DD_AT_POINT_AT_CAPTURE); } } /* ================ Team_FragBonuses Calculate the bonuses for flag defense, flag carrier defense, etc. Note that bonuses are not cumulative. You get one, they are in importance order. ================ */ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker) { int i; gentity_t *ent; int flag_pw, enemy_flag_pw; int otherteam; int tokens; gentity_t *flag, *carrier = NULL; char *c; vec3_t v1, v2; int team; // no bonus for fragging yourself or team mates if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker)) return; team = targ->client->sess.sessionTeam; otherteam = OtherTeam(targ->client->sess.sessionTeam); if (otherteam < 0) return; // whoever died isn't on a team // same team, if the flag at base, check to he has the enemy flag if (team == TEAM_RED) { flag_pw = PW_REDFLAG; enemy_flag_pw = PW_BLUEFLAG; } else { flag_pw = PW_BLUEFLAG; enemy_flag_pw = PW_REDFLAG; } if (g_gametype.integer == GT_1FCTF) { enemy_flag_pw = PW_NEUTRALFLAG; } // did the attacker frag the flag carrier? tokens = 0; if( g_gametype.integer == GT_HARVESTER ) { tokens = targ->client->ps.generic1; } if (targ->client->ps.powerups[enemy_flag_pw]) { attacker->client->pers.teamState.lastfraggedcarrier = level.time; AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS); attacker->client->pers.teamState.fragcarrier++; PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n", attacker->client->pers.netname, TeamName(team)); if(g_gametype.integer == GT_CTF) { G_LogPrintf( "CTF: %i %i %i: %s fragged %s's flag carrier!\n", attacker->client->ps.clientNum, team, 3, attacker->client->pers.netname, TeamName(team) ); } else if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s fragged %s's flag carrier!\n", level.roundNumber, attacker->client->ps.clientNum, team, 3, attacker->client->pers.netname, TeamName(team) ); } else if(g_gametype.integer == GT_1FCTF) { G_LogPrintf( "1fCTF: %i %i %i: %s fragged %s's flag carrier!\n", attacker->client->ps.clientNum, team, 3, attacker->client->pers.netname, TeamName(team) ); } // the target had the flag, clear the hurt carrier // field on the other team for (i = 0; i < g_maxclients.integer; i++) { ent = g_entities + i; if (ent->inuse && ent->client->sess.sessionTeam == otherteam) ent->client->pers.teamState.lasthurtcarrier = 0; } return; } // did the attacker frag a head carrier? other->client->ps.generic1 if (tokens) { attacker->client->pers.teamState.lastfraggedcarrier = level.time; AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens); attacker->client->pers.teamState.fragcarrier++; PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n", attacker->client->pers.netname, TeamName(team)); G_LogPrintf("HARVESTER: %i %i %i %i %i: %s fragged %s (%s) who had %i skulls.\n", attacker->client->ps.clientNum, team, 1, targ->client->ps.clientNum, tokens, attacker->client->pers.netname, targ->client->pers.netname,TeamName(team),tokens); // the target had the flag, clear the hurt carrier // field on the other team for (i = 0; i < g_maxclients.integer; i++) { ent = g_entities + i; if (ent->inuse && ent->client->sess.sessionTeam == otherteam) ent->client->pers.teamState.lasthurtcarrier = 0; } return; } if (targ->client->pers.teamState.lasthurtcarrier && level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT && !attacker->client->ps.powerups[flag_pw]) { // attacker is on the same team as the flag carrier and // fragged a guy who hurt our flag carrier AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS); attacker->client->pers.teamState.carrierdefense++; targ->client->pers.teamState.lasthurtcarrier = 0; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); team = attacker->client->sess.sessionTeam; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; } if (targ->client->pers.teamState.lasthurtcarrier && level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) { // attacker is on the same team as the skull carrier and AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS); attacker->client->pers.teamState.carrierdefense++; targ->client->pers.teamState.lasthurtcarrier = 0; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); team = attacker->client->sess.sessionTeam; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; } //We place the Double Domination bonus test here! This appears to be the best place to place them. if ( g_gametype.integer == GT_DOUBLE_D ) { if(attacker->client->sess.sessionTeam == level.pointStatusA ) { //Attack must defend point A //See how close attacker and target was to Point A: VectorSubtract(targ->r.currentOrigin, ddA->r.currentOrigin, v1); VectorSubtract(attacker->r.currentOrigin, ddA->r.currentOrigin, v2); if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddA->r.currentOrigin, targ->r.currentOrigin ) ) || ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddA->r.currentOrigin, attacker->r.currentOrigin ) ) ) && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) { //We defended point A //Was we dominating and maybe close to score? if(attacker->client->sess.sessionTeam == level.pointStatusB && level.time - level.timeTaken > (10-DD_CLOSE)*1000) AddScore(attacker, targ->r.currentOrigin, DD_POINT_DEFENCE_CLOSE_BONUS); else AddScore(attacker, targ->r.currentOrigin, DD_POINT_DEFENCE_BONUS); attacker->client->pers.teamState.basedefense++; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; //Return so we don't recieve credits for point B also } //We denfended point A } //Defend point A if(attacker->client->sess.sessionTeam == level.pointStatusB ) { //Attack must defend point B //See how close attacker and target was to Point B: VectorSubtract(targ->r.currentOrigin, ddB->r.currentOrigin, v1); VectorSubtract(attacker->r.currentOrigin, ddB->r.currentOrigin, v2); if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddB->r.currentOrigin, targ->r.currentOrigin ) ) || ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(ddB->r.currentOrigin, attacker->r.currentOrigin ) ) ) && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) { //We defended point B //Was we dominating and maybe close to score? if(attacker->client->sess.sessionTeam == level.pointStatusA && level.time - level.timeTaken > (10-DD_CLOSE)*1000) AddScore(attacker, targ->r.currentOrigin, DD_POINT_DEFENCE_CLOSE_BONUS); else AddScore(attacker, targ->r.currentOrigin, DD_POINT_DEFENCE_BONUS); attacker->client->pers.teamState.basedefense++; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; } //We denfended point B } //Defend point B return; //In double Domination we shall not go on, or we would test for team bases that we don't use } // flag and flag carrier area defense bonuses // we have to find the flag and carrier entities if( g_gametype.integer == GT_OBELISK ) { // find the team obelisk switch (attacker->client->sess.sessionTeam) { case TEAM_RED: c = "team_redobelisk"; break; case TEAM_BLUE: c = "team_blueobelisk"; break; default: return; } } else if (g_gametype.integer == GT_HARVESTER ) { // find the center obelisk c = "team_neutralobelisk"; } else { // find the flag switch (attacker->client->sess.sessionTeam) { case TEAM_RED: c = "team_CTF_redflag"; break; case TEAM_BLUE: c = "team_CTF_blueflag"; break; default: return; } // find attacker's team's flag carrier for (i = 0; i < g_maxclients.integer; i++) { carrier = g_entities + i; if (carrier->inuse && carrier->client->ps.powerups[flag_pw]) break; carrier = NULL; } } flag = NULL; while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) { if (!(flag->flags & FL_DROPPED_ITEM)) break; } if (!flag) return; // can't find attacker's flag // ok we have the attackers flag and a pointer to the carrier // check to see if we are defending the base's flag VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1); VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2); if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) || ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS && trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam && g_gametype.integer != GT_ELIMINATION && (g_gametype.integer != GT_CTF_ELIMINATION || !g_elimination_ctf_oneway.integer || ((level.eliminationSides+level.roundNumber)%2 == 0 && attacker->client->sess.sessionTeam == TEAM_BLUE ) || ((level.eliminationSides+level.roundNumber)%2 == 1 && attacker->client->sess.sessionTeam == TEAM_RED ) ) ) { // we defended the base flag AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS); attacker->client->pers.teamState.basedefense++; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; } if (carrier && carrier != attacker) { VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1); VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1); if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS && trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) || ( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS && trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) { AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS); attacker->client->pers.teamState.carrierdefense++; attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; if(!level.hadBots) ChallengeMessage(attacker,AWARD_DEFENCE); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 3, attacker->client->pers.netname, "DEFENCE" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_DEFEND; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; return; } } } /* ================ Team_CheckHurtCarrier Check to see if attacker hurt the flag carrier. Needed when handing out bonuses for assistance to flag carrier defense. ================ */ void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker) { int flag_pw; if (!targ->client || !attacker->client) return; if (targ->client->sess.sessionTeam == TEAM_RED) flag_pw = PW_BLUEFLAG; else flag_pw = PW_REDFLAG; // flags if (targ->client->ps.powerups[flag_pw] && targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam) attacker->client->pers.teamState.lasthurtcarrier = level.time; // skulls if (targ->client->ps.generic1 && targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam) attacker->client->pers.teamState.lasthurtcarrier = level.time; } gentity_t *Team_ResetFlag( int team ) { char *c; gentity_t *ent, *rent = NULL; switch (team) { case TEAM_RED: c = "team_CTF_redflag"; break; case TEAM_BLUE: c = "team_CTF_blueflag"; break; case TEAM_FREE: c = "team_CTF_neutralflag"; break; default: return NULL; } ent = NULL; while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) { if (ent->flags & FL_DROPPED_ITEM) G_FreeEntity(ent); else { rent = ent; RespawnItem(ent); } } Team_SetFlagStatus( team, FLAG_ATBASE ); return rent; } //Functions for Domination void Team_Dom_SpawnPoints( void ) { char *c; gentity_t *flag; int i; gitem_t *it; flag = NULL; if(dominationPointsSpawned) return; dominationPointsSpawned = qtrue; it = NULL; it = BG_FindItem ("Neutral domination point"); if(it == NULL) { PrintMsg( NULL, "No domination item\n"); return; } else { PrintMsg( NULL, "Domination item found\n"); } i = 0; c = "domination_point"; //return; Just to test, the lines below crashes game while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) { if(i>=MAX_DOMINATION_POINTS) break; //domination_points_names[i] = flag->message; if(flag->message) { Q_strncpyz(level.domination_points_names[i],flag->message,MAX_DOMINATION_POINTS_NAMES-1); PrintMsg( NULL, "Domination point \'%s\' found\n",level.domination_points_names[i]); } else { Q_strncpyz(level.domination_points_names[i],va("Point %i",i),MAX_DOMINATION_POINTS_NAMES-1); PrintMsg( NULL, "Domination point \'%s\' found (autonamed)\n",level.domination_points_names[i]); } dom_points[i] = G_Spawn(); VectorCopy( flag->r.currentOrigin, dom_points[i]->s.origin ); dom_points[i]->classname = it->classname; G_SpawnItem(dom_points[i], it); FinishSpawningItem(dom_points[i] ); i++; } level.domination_points_count = i; } int getDomPointNumber( gentity_t *point ) { int i; for(i=1;i=MAX_DOMINATION_POINTS) i = MAX_DOMINATION_POINTS - 1; it = NULL; VectorCopy( point->r.currentOrigin, origin ); if(team == TEAM_RED) { it = BG_FindItem ("Red domination point"); PrintMsg( NULL, "Red took \'%s\'\n",level.domination_points_names[i]); } else if(team == TEAM_BLUE) { it = BG_FindItem ("Blue domination point"); PrintMsg( NULL, "Blue took \'%s\'\n",level.domination_points_names[i]); } if (!it || it == NULL) { PrintMsg( NULL, "No item\n"); return; } G_FreeEntity(point); point = G_Spawn(); VectorCopy( origin, point->s.origin ); point->classname = it->classname; dom_points[i] = point; G_SpawnItem(point, it); FinishSpawningItem( point ); level.pointStatusDom[i] = team; G_LogPrintf( "DOM: %i %i %i %i: %s takes point %s!\n", clientnumber,i,0,team, TeamName(team),level.domination_points_names[i]); SendDominationPointsStatusMessageToAllClients(); } //Functions for Double Domination void Team_DD_RemovePointAgfx( void ) { if(ddA!=NULL) { G_FreeEntity(ddA); ddA = NULL; } } void Team_DD_RemovePointBgfx( void ) { if(ddB!=NULL) { G_FreeEntity(ddB); ddB = NULL; } } void Team_DD_makeA2team( gentity_t *target, int team ) { gitem_t *it; //gentity_t *it_ent; Team_DD_RemovePointAgfx(); it = NULL; if(team == TEAM_NONE) return; if(team == TEAM_RED) it = BG_FindItem ("Point A (Red)"); if(team == TEAM_BLUE) it = BG_FindItem ("Point A (Blue)"); if(team == TEAM_FREE) it = BG_FindItem ("Point A (White)"); if (!it || it == NULL) { PrintMsg( NULL, "No item\n"); return; } ddA = G_Spawn(); VectorCopy( target->r.currentOrigin, ddA->s.origin ); ddA->classname = it->classname; G_SpawnItem(ddA, it); FinishSpawningItem(ddA ); } void Team_DD_makeB2team( gentity_t *target, int team ) { gitem_t *it; //gentity_t *it_ent; Team_DD_RemovePointBgfx(); it = NULL; if(team == TEAM_NONE) return; if(team == TEAM_RED) it = BG_FindItem ("Point B (Red)"); if(team == TEAM_BLUE) it = BG_FindItem ("Point B (Blue)"); if(team == TEAM_FREE) it = BG_FindItem ("Point B (White)"); if (!it || it == NULL) { PrintMsg( NULL, "No item\n"); return; } ddB = G_Spawn(); VectorCopy( target->r.currentOrigin, ddB->s.origin ); ddB->classname = it->classname; G_SpawnItem(ddB, it); FinishSpawningItem(ddB ); } void Team_ResetFlags( void ) { if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTF_ELIMINATION) { Team_ResetFlag( TEAM_RED ); Team_ResetFlag( TEAM_BLUE ); } else if( g_gametype.integer == GT_1FCTF ) { Team_ResetFlag( TEAM_FREE ); } } void Team_ReturnFlagSound( gentity_t *ent, int team ) { gentity_t *te; if (ent == NULL) { G_Printf ("Warning: NULL passed to Team_ReturnFlagSound\n"); return; } //See if we are during CTF_ELIMINATION warmup if((level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer)&&g_gametype.integer == GT_CTF_ELIMINATION) return; te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND ); if( team == TEAM_BLUE ) { te->s.eventParm = GTS_RED_RETURN; } else { te->s.eventParm = GTS_BLUE_RETURN; } te->r.svFlags |= SVF_BROADCAST; } void Team_TakeFlagSound( gentity_t *ent, int team ) { gentity_t *te; if (ent == NULL) { G_Printf ("Warning: NULL passed to Team_TakeFlagSound\n"); return; } // only play sound when the flag was at the base // or not picked up the last 10 seconds switch(team) { case TEAM_RED: if( teamgame.blueStatus != FLAG_ATBASE ) { if (teamgame.blueTakenTime > level.time - 10000 && g_gametype.integer != GT_CTF_ELIMINATION) return; } teamgame.blueTakenTime = level.time; break; case TEAM_BLUE: // CTF if( teamgame.redStatus != FLAG_ATBASE ) { if (teamgame.redTakenTime > level.time - 10000 && g_gametype.integer != GT_CTF_ELIMINATION) return; } teamgame.redTakenTime = level.time; break; } te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND ); if( team == TEAM_BLUE ) { te->s.eventParm = GTS_RED_TAKEN; } else { te->s.eventParm = GTS_BLUE_TAKEN; } te->r.svFlags |= SVF_BROADCAST; } void Team_CaptureFlagSound( gentity_t *ent, int team ) { gentity_t *te; if (ent == NULL) { G_Printf ("Warning: NULL passed to Team_CaptureFlagSound\n"); return; } te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND ); if( team == TEAM_BLUE ) { te->s.eventParm = GTS_BLUE_CAPTURE; } else { te->s.eventParm = GTS_RED_CAPTURE; } te->r.svFlags |= SVF_BROADCAST; } void Team_ReturnFlag( int team ) { Team_ReturnFlagSound(Team_ResetFlag(team), team); if( team == TEAM_FREE ) { PrintMsg(NULL, "The flag has returned!\n" ); if(g_gametype.integer == GT_1FCTF) { G_LogPrintf( "1FCTF: %i %i %i: The flag was returned!\n", -1, -1, 2 ); } } else { PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team)); if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF: %i %i %i: The %s flag was returned!\n", -1, team, 2, TeamName(team) ); } else if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: The %s flag was returned!\n", level.roundNumber, -1, team, 2, TeamName(team) ); } } } void Team_FreeEntity( gentity_t *ent ) { if( ent->item->giTag == PW_REDFLAG ) { Team_ReturnFlag( TEAM_RED ); } else if( ent->item->giTag == PW_BLUEFLAG ) { Team_ReturnFlag( TEAM_BLUE ); } else if( ent->item->giTag == PW_NEUTRALFLAG ) { Team_ReturnFlag( TEAM_FREE ); } } /* ============== Team_DroppedFlagThink Automatically set in Launch_Item if the item is one of the flags Flags are unique in that if they are dropped, the base flag must be respawned when they time out ============== */ void Team_DroppedFlagThink(gentity_t *ent) { int team = TEAM_FREE; if( ent->item->giTag == PW_REDFLAG ) { team = TEAM_RED; } else if( ent->item->giTag == PW_BLUEFLAG ) { team = TEAM_BLUE; } else if( ent->item->giTag == PW_NEUTRALFLAG ) { team = TEAM_FREE; } Team_ReturnFlagSound( Team_ResetFlag( team ), team ); // Reset Flag will delete this entity } /* Update DD points */ void updateDDpoints(void) { //teamgame.redStatus = -1; // Invalid to force update Team_SetFlagStatus( TEAM_RED, level.pointStatusA ); //teamgame.blueStatus = -1; // Invalid to force update Team_SetFlagStatus( TEAM_BLUE, level.pointStatusB ); } /* ============== Team_SpawnDoubleDominationPoints ============== */ int Team_SpawnDoubleDominationPoints ( void ) { gentity_t *ent; level.pointStatusA = TEAM_FREE; level.pointStatusB = TEAM_FREE; updateDDpoints(); ent = NULL; if ((ent = G_Find (ent, FOFS(classname), "team_CTF_redflag")) != NULL) { Team_DD_makeA2team( ent, TEAM_FREE ); } ent = NULL; if ((ent = G_Find (ent, FOFS(classname), "team_CTF_blueflag")) != NULL) { Team_DD_makeB2team( ent, TEAM_FREE ); } return 1; } /* ============== Team_RemoveDoubleDominationPoints ============== */ int Team_RemoveDoubleDominationPoints ( void ) { level.pointStatusA = TEAM_NONE; level.pointStatusB = TEAM_NONE; updateDDpoints(); Team_DD_makeA2team( NULL, TEAM_NONE ); Team_DD_makeB2team( NULL, TEAM_NONE ); return 1; } /* ============== Team_TouchDoubleDominationPoint ============== */ //team is the either TEAM_RED(A) or TEAM_BLUE(B) int Team_TouchDoubleDominationPoint( gentity_t *ent, gentity_t *other, int team ) { gclient_t *cl = other->client; qboolean otherDominating, isClose; int clientTeam = cl->sess.sessionTeam; int otherTeam; int score; //Used to add the scores together if(clientTeam == TEAM_RED) otherTeam = TEAM_BLUE; else otherTeam = TEAM_RED; otherDominating = qfalse; isClose = qfalse; if(level.pointStatusA == otherTeam && level.pointStatusB == otherTeam) { otherDominating = qtrue; if(level.time - level.timeTaken > (10-DD_CLOSE)*1000) isClose = qtrue; } if(team == TEAM_RED) //We have touched point A { if(TEAM_NONE == level.pointStatusA) return 0; //Haven't spawned yet if(clientTeam == level.pointStatusA) return 0; //If we already have the flag //if we didn't have the point, then we have now! level.pointStatusA = clientTeam; PrintMsg( NULL, "%s" S_COLOR_WHITE " (%s) took control of A!\n", cl->pers.netname, TeamName(clientTeam) ); Team_DD_makeA2team( ent, clientTeam ); G_LogPrintf( "DD: %i %i %i: %s took point A for %s!\n", cl->ps.clientNum, clientTeam, 0, cl->pers.netname, TeamName(clientTeam) ); //Give personal score score = DD_POINT_CAPTURE; //Base score for capture if(otherDominating){ score += DD_POINT_CAPTURE_BREAK; if(isClose) score += DD_POINT_CAPTURE_CLOSE; } AddScore(other, ent->r.currentOrigin, score); //Do we also have point B? if(clientTeam == level.pointStatusB) { //We are dominating! level.timeTaken = level.time; //At this time PrintMsg( NULL, "%s" S_COLOR_WHITE " is dominating!\n", TeamName(clientTeam) ); SendDDtimetakenMessageToAllClients(); } } if(team == TEAM_BLUE) //We have touched point B { if(TEAM_NONE == level.pointStatusB) return 0; //Haven't spawned yet if(clientTeam == level.pointStatusB) return 0; //If we already have the flag //if we didn't have the point, then we have now! level.pointStatusB = clientTeam; PrintMsg( NULL, "%s" S_COLOR_WHITE " (%s) took control of B!\n", cl->pers.netname, TeamName(clientTeam) ); Team_DD_makeB2team( ent, clientTeam ); G_LogPrintf( "DD: %i %i %i: %s took point B for %s!\n", cl->ps.clientNum, clientTeam, 1, cl->pers.netname, TeamName(clientTeam) ); //Give personal score score = DD_POINT_CAPTURE; //Base score for capture if(otherDominating){ score += DD_POINT_CAPTURE_BREAK; if(isClose) score += DD_POINT_CAPTURE_CLOSE; } AddScore(other, ent->r.currentOrigin, score); //Do we also have point A? if(clientTeam == level.pointStatusA) { //We are dominating! level.timeTaken = level.time; //At this time PrintMsg( NULL, "%s" S_COLOR_WHITE " is dominating!\n", TeamName(clientTeam) ); SendDDtimetakenMessageToAllClients(); } } updateDDpoints(); return 0; } /* ============== Team_TouchOurFlag ============== */ int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) { int i; gentity_t *player; gclient_t *cl = other->client; int enemy_flag; if( g_gametype.integer == GT_1FCTF ) { enemy_flag = PW_NEUTRALFLAG; } else { if (cl->sess.sessionTeam == TEAM_RED) { enemy_flag = PW_BLUEFLAG; } else { enemy_flag = PW_REDFLAG; } if ( ent->flags & FL_DROPPED_ITEM ) { // hey, its not home. return it by teleporting it back PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", cl->pers.netname, TeamName(team)); AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS); if(g_gametype.integer == GT_CTF) { G_LogPrintf( "CTF: %i %i %i: %s returned the %s flag!\n", cl->ps.clientNum, team, 2, cl->pers.netname, TeamName(team) ); } else if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s returned the %s flag!\n", level.roundNumber, cl->ps.clientNum, team, 2, cl->pers.netname, TeamName(team) ); } other->client->pers.teamState.flagrecovery++; other->client->pers.teamState.lastreturnedflag = level.time; //ResetFlag will remove this entity! We must return zero Team_ReturnFlagSound(Team_ResetFlag(team), team); return 0; } } // the flag is at home base. if the player has the enemy // flag, he's just won! if (!cl->ps.powerups[enemy_flag]) return 0; // We don't have the flag if( g_gametype.integer == GT_1FCTF ) { PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the flag!\n", cl->pers.netname ); G_LogPrintf( "1FCTF: %i %i %i: %s captured the flag!\n", cl->ps.clientNum, -1, 1, cl->pers.netname ); } else { PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team))); if(g_gametype.integer == GT_CTF) G_LogPrintf( "CTF: %i %i %i: %s captured the %s flag!\n", cl->ps.clientNum, OtherTeam(team), 1, cl->pers.netname, TeamName(OtherTeam(team)) ); if(g_gametype.integer == GT_CTF_ELIMINATION) G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s captured the %s flag!\n", level.roundNumber, cl->ps.clientNum, OtherTeam(team), 1, cl->pers.netname, TeamName(OtherTeam(team)) ); } cl->ps.powerups[enemy_flag] = 0; teamgame.last_flag_capture = level.time; teamgame.last_capture_team = team; // Increase the team's score AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1); Team_ForceGesture(other->client->sess.sessionTeam); //If CTF Elimination, stop the round: if(g_gametype.integer==GT_CTF_ELIMINATION) { EndEliminationRound(); } other->client->pers.teamState.captures++; // add the sprite over the player's head other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); other->client->ps.eFlags |= EF_AWARD_CAP; other->client->rewardTime = level.time + REWARD_SPRITE_TIME; other->client->ps.persistant[PERS_CAPTURES]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", other->client->ps.clientNum, 4, other->client->pers.netname, "CAPTURE" ); if(TeamCount(-1,TEAM_RED) && TeamCount(-1,TEAM_BLUE) && !level.hadBots) ChallengeMessage(other,AWARD_CAPTURE); // other gets another 10 frag bonus AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS); Team_CaptureFlagSound( ent, team ); // Ok, let's do the player loop, hand out the bonuses for (i = 0; i < g_maxclients.integer; i++) { player = &g_entities[i]; if (!player->inuse || player == other) continue; if (player->client->sess.sessionTeam != cl->sess.sessionTeam) { player->client->pers.teamState.lasthurtcarrier = -5; } else if (player->client->sess.sessionTeam == cl->sess.sessionTeam) { if (player != other) AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS); // award extra points for capture assists if (player->client->pers.teamState.lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) { AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS); other->client->pers.teamState.assists++; player->client->ps.persistant[PERS_ASSIST_COUNT]++; if(!level.hadBots) ChallengeMessage(player,AWARD_ASSIST); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", player->client->ps.clientNum, 5, player->client->pers.netname, "ASSIST" ); // add the sprite over the player's head player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); player->client->ps.eFlags |= EF_AWARD_ASSIST; player->client->rewardTime = level.time + REWARD_SPRITE_TIME; } if (player->client->pers.teamState.lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) { AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS); other->client->pers.teamState.assists++; player->client->ps.persistant[PERS_ASSIST_COUNT]++; if(!level.hadBots) ChallengeMessage(player,AWARD_ASSIST); G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", player->client->ps.clientNum, 5, player->client->pers.netname, "ASSIST" ); // add the sprite over the player's head player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); player->client->ps.eFlags |= EF_AWARD_ASSIST; player->client->rewardTime = level.time + REWARD_SPRITE_TIME; } } } Team_ResetFlags(); CalculateRanks(); return 0; // Do not respawn this automatically } int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) { gclient_t *cl = other->client; if( g_gametype.integer == GT_1FCTF ) { PrintMsg (NULL, "%s" S_COLOR_WHITE " got the flag!\n", other->client->pers.netname ); G_LogPrintf( "1FCTF: %i %i %i: %s got the flag!\n", cl->ps.clientNum, team, 0, cl->pers.netname); cl->ps.powerups[PW_NEUTRALFLAG] = INT_MAX; // flags never expire if( team == TEAM_RED ) { Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_RED ); } else { Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_BLUE ); } } else{ PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n", other->client->pers.netname, TeamName(team)); if(g_gametype.integer == GT_CTF) { G_LogPrintf( "CTF: %i %i %i: %s got the %s flag!\n", cl->ps.clientNum, team, 0, cl->pers.netname, TeamName(team)); } else if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s got the %s flag!\n", level.roundNumber, cl->ps.clientNum, team, 0, cl->pers.netname, TeamName(team)); } if (team == TEAM_RED) cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire else cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire Team_SetFlagStatus( team, FLAG_TAKEN ); } AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS); cl->pers.teamState.flagsince = level.time; Team_TakeFlagSound( ent, team ); return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED } int Pickup_Team( gentity_t *ent, gentity_t *other ) { int team; gclient_t *cl = other->client; if( g_gametype.integer == GT_OBELISK ) { // there are no team items that can be picked up in obelisk G_FreeEntity( ent ); return 0; } if( g_gametype.integer == GT_HARVESTER ) { // the only team items that can be picked up in harvester are the cubes if( ent->spawnflags != cl->sess.sessionTeam ) { cl->ps.generic1 += 1; //Skull pickedup G_LogPrintf("HARVESTER: %i %i %i %i %i: %s picked up a skull.\n", cl->ps.clientNum,cl->sess.sessionTeam,3,-1,1, cl->pers.netname); } else { G_LogPrintf("HARVESTER: %i %i %i %i %i: %s destroyed a skull.\n,", cl->ps.clientNum,cl->sess.sessionTeam,2,-1,1, cl->pers.netname); } G_FreeEntity( ent ); //Destory skull return 0; } if ( g_gametype.integer == GT_DOMINATION ) { Team_Dom_TakePoint(ent, cl->sess.sessionTeam,cl->ps.clientNum); return 0; } // figure out what team this flag is if( strcmp(ent->classname, "team_CTF_redflag") == 0 ) { team = TEAM_RED; } else if( strcmp(ent->classname, "team_CTF_blueflag") == 0 ) { team = TEAM_BLUE; } else if( strcmp(ent->classname, "team_CTF_neutralflag") == 0 ) { team = TEAM_FREE; } else { PrintMsg ( other, "Don't know what team the flag is on.\n"); return 0; } if( g_gametype.integer == GT_1FCTF ) { if( team == TEAM_FREE ) { return Team_TouchEnemyFlag( ent, other, cl->sess.sessionTeam ); } if( team != cl->sess.sessionTeam) { return Team_TouchOurFlag( ent, other, cl->sess.sessionTeam ); } return 0; } if( g_gametype.integer == GT_DOUBLE_D) { return Team_TouchDoubleDominationPoint( ent, other, team ); } // GT_CTF if( team == cl->sess.sessionTeam) { return Team_TouchOurFlag( ent, other, team ); } return Team_TouchEnemyFlag( ent, other, team ); } /* =========== Team_GetLocation Report a location for the player. Uses placed nearby target_location entities ============ */ gentity_t *Team_GetLocation(gentity_t *ent) { gentity_t *eloc, *best; float bestlen, len; vec3_t origin; best = NULL; bestlen = 3*8192.0*8192.0; VectorCopy( ent->r.currentOrigin, origin ); for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) { len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] ) + ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] ) + ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] ); if ( len > bestlen ) { continue; } if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) { continue; } bestlen = len; best = eloc; } return best; } /* =========== Team_GetLocation Report a location for the player. Uses placed nearby target_location entities ============ */ qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen) { gentity_t *best; best = Team_GetLocation( ent ); if (!best) return qfalse; if (best->count) { if (best->count < 0) best->count = 0; if (best->count > 7) best->count = 7; Com_sprintf(loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message ); } else Com_sprintf(loc, loclen, "%s", best->message); return qtrue; } /*---------------------------------------------------------------------------*/ /* ================ SelectRandomDeathmatchSpawnPoint go to a random point that doesn't telefrag ================ */ #define MAX_TEAM_SPAWN_POINTS 32 gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_TEAM_SPAWN_POINTS]; char *classname; if(g_gametype.integer == GT_ELIMINATION) { //change sides every round if((level.roundNumber+level.eliminationSides)%2==1){ if(team == TEAM_RED) team = TEAM_BLUE; else if(team == TEAM_BLUE) team = TEAM_RED; } } if (teamstate == TEAM_BEGIN) { if (team == TEAM_RED) classname = "team_CTF_redplayer"; else if (team == TEAM_BLUE) classname = "team_CTF_blueplayer"; else return NULL; } else { if (team == TEAM_RED) classname = "team_CTF_redspawn"; else if (team == TEAM_BLUE) classname = "team_CTF_bluespawn"; else return NULL; } count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } spots[ count ] = spot; if (++count == MAX_TEAM_SPAWN_POINTS) break; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), classname); } selection = rand() % count; return spots[ selection ]; } /* ================ SelectRandomDDSpawnPoint go to a random Double Domination Spawn Point ================ */ #define MAX_TEAM_SPAWN_POINTS 32 gentity_t *SelectRandomDDSpawnPoint( void ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_TEAM_SPAWN_POINTS]; char *classname; classname = "info_player_dd"; count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } spots[ count ] = spot; if (++count == MAX_TEAM_SPAWN_POINTS) break; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), classname); } selection = rand() % count; return spots[ selection ]; } gentity_t *SelectRandomTeamDDSpawnPoint( team_t team ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_TEAM_SPAWN_POINTS]; char *classname; if(team == TEAM_RED) classname = "info_player_dd_red"; else classname = "info_player_dd_blue"; count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } spots[ count ] = spot; if (++count == MAX_TEAM_SPAWN_POINTS) break; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), classname); } selection = rand() % count; return spots[ selection ]; } /* =========== SelectCTFSpawnPoint ============ */ gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = SelectRandomTeamSpawnPoint ( teamstate, team ); if (!spot) { return SelectSpawnPoint( vec3_origin, origin, angles ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } /* =========== SelectDoubleDominationSpawnPoint ============ */ gentity_t *SelectDoubleDominationSpawnPoint ( team_t team, vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = SelectRandomTeamDDSpawnPoint( team ); if(!spot) { spot = SelectRandomDDSpawnPoint ( ); } if (!spot) { return SelectSpawnPoint( vec3_origin, origin, angles ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } /*---------------------------------------------------------------------------*/ static int QDECL SortClients( const void *a, const void *b ) { return *(int *)a - *(int *)b; } /* ================== TeamplayLocationsMessage Format: clientNum location health armor weapon powerups ================== */ void TeamplayInfoMessage( gentity_t *ent ) { char entry[1024]; char string[8192]; int stringlength; int i, j; gentity_t *player; int cnt; int h, a, w; int clients[TEAM_MAXOVERLAY]; if ( ! ent->client->pers.teamInfo ) return; // figure out what client should be on the display // we are limited to 8, but we want to use the top eight players // but in client order (so they don't keep changing position on the overlay) for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) { player = g_entities + level.sortedClients[i]; if (player->inuse && player->client->sess.sessionTeam == ent->client->sess.sessionTeam ) { clients[cnt++] = level.sortedClients[i]; } } // We have the top eight players, sort them by clientNum qsort( clients, cnt, sizeof( clients[0] ), SortClients ); // send the latest information on all clients string[0] = 0; stringlength = 0; for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) { player = g_entities + i; if (player->inuse && player->client->sess.sessionTeam == ent->client->sess.sessionTeam ) { h = player->client->ps.stats[STAT_HEALTH]; a = player->client->ps.stats[STAT_ARMOR]; w = player->client->ps.weapon; if(player->client->isEliminated) { h = 0; a = 0; w = 0; } if (h < 0) h = 0; if (a < 0) a = 0; Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i", // level.sortedClients[i], player->client->pers.teamState.location, h, a, i, player->client->pers.teamState.location, h, a, w, player->s.powerups); j = strlen(entry); if (stringlength + j > sizeof(string)) break; strcpy (string + stringlength, entry); stringlength += j; cnt++; } } trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) ); } void CheckTeamStatus(void) { int i; gentity_t *loc, *ent; if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) { level.lastTeamLocationTime = level.time; for (i = 0; i < g_maxclients.integer; i++) { ent = g_entities + i; if ( ent->client->pers.connected != CON_CONNECTED ) { continue; } if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) { loc = Team_GetLocation( ent ); if (loc) ent->client->pers.teamState.location = loc->health; else ent->client->pers.teamState.location = 0; } } for (i = 0; i < g_maxclients.integer; i++) { ent = g_entities + i; if ( ent->client->pers.connected != CON_CONNECTED ) { continue; } if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) { TeamplayInfoMessage( ent ); } } } } /*-----------------------------------------------------------------*/ /*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32) Only in CTF games. Red players spawn here at game start. */ void SP_team_CTF_redplayer( gentity_t *ent ) { } /*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32) Only in CTF games. Blue players spawn here at game start. */ void SP_team_CTF_blueplayer( gentity_t *ent ) { } /*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32) potential spawning position for red team in CTF games. Targets will be fired when someone spawns in on them. */ void SP_team_CTF_redspawn(gentity_t *ent) { } /*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32) potential spawning position for blue team in CTF games. Targets will be fired when someone spawns in on them. */ void SP_team_CTF_bluespawn(gentity_t *ent) { } /* ================ Obelisks ================ */ static void ObeliskHealthChange( int team, int health ) { int currentPercentage; int percentage = (health*100)/g_obeliskHealth.integer; if(percentage < 0) percentage = 0; currentPercentage = level.healthRedObelisk; if(team != TEAM_RED) currentPercentage = level.healthBlueObelisk; if(percentage != currentPercentage) { if(team == TEAM_RED) { level.healthRedObelisk = percentage; } else { level.healthBlueObelisk = percentage; } level.MustSendObeliskHealth = qtrue; //G_Printf("Obelisk health %i %i\n",team,percentage); ObeliskHealthMessage(); } } static void ObeliskRegen( gentity_t *self ) { self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000; ObeliskHealthChange(self->spawnflags,self->health); if( self->health >= g_obeliskHealth.integer ) { return; } G_AddEvent( self, EV_POWERUP_REGEN, 0 ); self->health += g_obeliskRegenAmount.integer; if ( self->health > g_obeliskHealth.integer ) { self->health = g_obeliskHealth.integer; } self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer; self->activator->s.frame = 0; } static void ObeliskRespawn( gentity_t *self ) { self->takedamage = qtrue; self->health = g_obeliskHealth.integer; self->think = ObeliskRegen; self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000; self->activator->s.frame = 0; } static void ObeliskDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) { int otherTeam; otherTeam = OtherTeam( self->spawnflags ); AddTeamScore(self->s.pos.trBase, otherTeam, 1); Team_ForceGesture(otherTeam); CalculateRanks(); self->takedamage = qfalse; self->think = ObeliskRespawn; self->nextthink = level.time + g_obeliskRespawnDelay.integer * 1000; self->activator->s.modelindex2 = 0xff; self->activator->s.frame = 2; G_AddEvent( self->activator, EV_OBELISKEXPLODE, 0 ); AddScore(attacker, self->r.currentOrigin, CTF_CAPTURE_BONUS); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_CAP; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; attacker->client->ps.persistant[PERS_CAPTURES]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 4, attacker->client->pers.netname, "CAPTURE" ); G_LogPrintf("OBELISK: %i %i %i %i: %s destroyed the enemy obelisk.\n", attacker->client->ps.clientNum,attacker->client->sess.sessionTeam,3,0, attacker->client->pers.netname); if(TeamCount(-1,TEAM_RED) && TeamCount(-1,TEAM_BLUE) && !level.hadBots) ChallengeMessage(attacker,AWARD_CAPTURE); ObeliskHealthChange(self->spawnflags,self->health); teamgame.redObeliskAttackedTime = 0; teamgame.blueObeliskAttackedTime = 0; } static void ObeliskTouch( gentity_t *self, gentity_t *other, trace_t *trace ) { int tokens,i; if ( !other->client ) { return; } if ( OtherTeam(other->client->sess.sessionTeam) != self->spawnflags ) { return; } tokens = other->client->ps.generic1; if( tokens <= 0 ) { return; } PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i skull%s.\n", other->client->pers.netname, tokens, tokens>1 ? "s" : "" ); G_LogPrintf("HARVESTER: %i %i %i %i %i: %s brought in %i skull%s for %s\n", other->client->ps.clientNum,other->client->sess.sessionTeam,0,-1,tokens, other->client->pers.netname,tokens, tokens>1 ? "s" : "", TeamName(other->client->sess.sessionTeam)); AddTeamScore(self->s.pos.trBase, other->client->sess.sessionTeam, tokens); Team_ForceGesture(other->client->sess.sessionTeam); AddScore(other, self->r.currentOrigin, CTF_CAPTURE_BONUS*tokens); // add the sprite over the player's head other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); other->client->ps.eFlags |= EF_AWARD_CAP; other->client->rewardTime = level.time + REWARD_SPRITE_TIME; other->client->ps.persistant[PERS_CAPTURES] += tokens; for(i = 0;iclient->ps.clientNum, 4, other->client->pers.netname, "CAPTURE" ); if(TeamCount(-1,TEAM_RED) && TeamCount(-1,TEAM_BLUE) && !level.hadBots) ChallengeMessage(other,AWARD_CAPTURE); } other->client->ps.generic1 = 0; CalculateRanks(); Team_CaptureFlagSound( self, self->spawnflags ); } static void ObeliskPain( gentity_t *self, gentity_t *attacker, int damage ) { int actualDamage = damage / 10; if (actualDamage <= 0) { actualDamage = 1; } self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer; if (!self->activator->s.frame) { G_AddEvent(self, EV_OBELISKPAIN, 0); } self->activator->s.frame = 1; AddScore(attacker, self->r.currentOrigin, actualDamage); G_LogPrintf("OBELISK: %i %i %i %i: %s dealt %i damage to the enemy obelisk.\n", attacker->client->ps.clientNum,attacker->client->sess.sessionTeam,1,actualDamage, attacker->client->pers.netname,actualDamage); } gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { trace_t tr; vec3_t dest; gentity_t *ent; ent = G_Spawn(); VectorCopy( origin, ent->s.origin ); VectorCopy( origin, ent->s.pos.trBase ); VectorCopy( origin, ent->r.currentOrigin ); VectorSet( ent->r.mins, -15, -15, 0 ); VectorSet( ent->r.maxs, 15, 15, 87 ); ent->s.eType = ET_GENERAL; ent->flags = FL_NO_KNOCKBACK; if( g_gametype.integer == GT_OBELISK ) { ent->r.contents = CONTENTS_SOLID; ent->takedamage = qtrue; ent->health = g_obeliskHealth.integer; ent->die = ObeliskDie; ent->pain = ObeliskPain; ent->think = ObeliskRegen; ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000; } if( g_gametype.integer == GT_HARVESTER ) { ent->r.contents = CONTENTS_TRIGGER; ent->touch = ObeliskTouch; } if ( spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // mappers like to put them exactly on the floor, but being coplanar // will sometimes show up as starting in solid, so lif it up one pixel ent->s.origin[2] += 1; // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID ); if ( tr.startsolid ) { ent->s.origin[2] -= 1; G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) ); ent->s.groundEntityNum = ENTITYNUM_NONE; G_SetOrigin( ent, ent->s.origin ); } else { // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } } ent->spawnflags = team; trap_LinkEntity( ent ); return ent; } /*QUAKED team_redobelisk (1 0 0) (-16 -16 0) (16 16 8) */ void SP_team_redobelisk( gentity_t *ent ) { gentity_t *obelisk; if ( g_gametype.integer <= GT_TEAM || g_ffa_gt>0) { G_FreeEntity(ent); return; } ent->s.eType = ET_TEAM; if ( g_gametype.integer == GT_OBELISK ) { obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags ); obelisk->activator = ent; // initial obelisk health value ent->s.modelindex2 = 0xff; ent->s.frame = 0; } if ( g_gametype.integer == GT_HARVESTER ) { obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags ); obelisk->activator = ent; } ent->s.modelindex = TEAM_RED; trap_LinkEntity(ent); } /*QUAKED team_blueobelisk (0 0 1) (-16 -16 0) (16 16 88) */ void SP_team_blueobelisk( gentity_t *ent ) { gentity_t *obelisk; if ( g_gametype.integer <= GT_TEAM || g_ffa_gt>0) { G_FreeEntity(ent); return; } ent->s.eType = ET_TEAM; if ( g_gametype.integer == GT_OBELISK ) { obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags ); obelisk->activator = ent; // initial obelisk health value ent->s.modelindex2 = 0xff; ent->s.frame = 0; } if ( g_gametype.integer == GT_HARVESTER ) { obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags ); obelisk->activator = ent; } ent->s.modelindex = TEAM_BLUE; trap_LinkEntity(ent); } /*QUAKED team_neutralobelisk (0 0 1) (-16 -16 0) (16 16 88) */ void SP_team_neutralobelisk( gentity_t *ent ) { if ( g_gametype.integer != GT_1FCTF && g_gametype.integer != GT_HARVESTER ) { G_FreeEntity(ent); return; } ent->s.eType = ET_TEAM; if ( g_gametype.integer == GT_HARVESTER) { neutralObelisk = SpawnObelisk( ent->s.origin, TEAM_FREE, ent->spawnflags); neutralObelisk->spawnflags = TEAM_FREE; } ent->s.modelindex = TEAM_FREE; trap_LinkEntity(ent); } /* ================ CheckObeliskAttack ================ */ qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker ) { gentity_t *te; // if this really is an obelisk if( obelisk->die != ObeliskDie ) { return qfalse; } // if the attacker is a client if( !attacker->client ) { return qfalse; } // if the obelisk is on the same team as the attacker then don't hurt it if( obelisk->spawnflags == attacker->client->sess.sessionTeam ) { return qtrue; } // obelisk may be hurt // if not played any sounds recently if ((obelisk->spawnflags == TEAM_RED && teamgame.redObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) || (obelisk->spawnflags == TEAM_BLUE && teamgame.blueObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ) { // tell which obelisk is under attack te = G_TempEntity( obelisk->s.pos.trBase, EV_GLOBAL_TEAM_SOUND ); if( obelisk->spawnflags == TEAM_RED ) { te->s.eventParm = GTS_REDOBELISK_ATTACKED; teamgame.redObeliskAttackedTime = level.time; } else { te->s.eventParm = GTS_BLUEOBELISK_ATTACKED; teamgame.blueObeliskAttackedTime = level.time; } te->r.svFlags |= SVF_BROADCAST; } return qfalse; } /* ================ ShuffleTeams *Randomizes the teams based on a type of function and then restarts the map *Currently there is only one type so type is ignored. You can add total randomizaion or waighted randomization later. * *The function will split the teams like this: *1. Red team *2. Blue team *3. Blue team *4. Red team *5. Go to 1 ================ */ void ShuffleTeams(void) { int i; int assignedClients=1, nextTeam=TEAM_RED; if ( g_gametype.integer < GT_TEAM || g_ffa_gt==1) return; //Can only shuffle team games! for( i=0;i < level.numConnectedClients; i++ ) { if( g_entities[ &level.clients[level.sortedClients[i]] - level.clients].r.svFlags & SVF_BOT) continue; //Don't sort bots... they are always equal if(level.clients[level.sortedClients[i]].sess.sessionTeam==TEAM_RED || level.clients[level.sortedClients[i]].sess.sessionTeam==TEAM_BLUE ) { //For every second client we chenge team. But we do it a little of to make it slightly more fair if(assignedClients>1) { assignedClients=0; if(nextTeam == TEAM_RED) nextTeam = TEAM_BLUE; else nextTeam = TEAM_RED; } //Set the team //We do not run all the logic because we shall run map_restart in a moment. level.clients[level.sortedClients[i]].sess.sessionTeam = nextTeam; ClientUserinfoChanged( level.sortedClients[i] ); ClientBegin( level.sortedClients[i] ); assignedClients++; } } //Restart! trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); } openarena_0.8.8.orig/code/game/g_playerstore.c0000644000175000017500000000747211656310264020106 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2008-2009 Poul Sander This file is part of Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" #define MAX_PLAYERS_STORED 32 #define GUID_SIZE 32 typedef struct { char guid[GUID_SIZE+1]; //The guid is 32 chars long int age; //int that grows each time a new player is stored. The lowest number is always replaced. Reset to 0 then retrieved. int persistant[MAX_PERSISTANT]; //This is the only information we need to save int timePlayed; int accuracy[WP_NUM_WEAPONS][2]; } playerstore_t; static playerstore_t playerstore[MAX_PLAYERS_STORED]; static int nextAge; /* *Resets the player store. Should be called everytime game.qvm is loaded. */ void PlayerStoreInit( void ) { memset(playerstore,0,sizeof(playerstore)); nextAge = 1; } void PlayerStore_store(char* guid, playerState_t ps) { int place2store = -1; int lowestAge = 32000; int i; if(strlen(guid)<32) { G_LogPrintf("Playerstore: Failed to store player. Invalid guid: %s\n",guid); return; } for(i=0;ipersistant,playerstore[i].persistant,sizeof(int[MAX_PERSISTANT])); memcpy(level.clients[ps->clientNum].accuracy, playerstore[i].accuracy,sizeof(playerstore[0].accuracy) ); level.clients[ps->clientNum].pers.enterTime = level.time - playerstore[i].timePlayed; //Never ever restore a player with negative score if(ps->persistant[PERS_SCORE]<0) ps->persistant[PERS_SCORE]=0; playerstore[i].age = -1; G_LogPrintf("Restored player with guid: %s\n",guid); return; } } G_LogPrintf("Playerstore: Nothing to restore. Guid: %s\n",guid); } openarena_0.8.8.orig/code/game/bg_alloc.c0000644000175000017500000001434211656310264016763 0ustar smcvsmcv//KK-OAX /* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2000-2006 Tim Angus This file is part of the Open Arena source code. Copied from Tremulous under GPL version 2 including any later version. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../qcommon/q_shared.h" #include "bg_public.h" #include "g_local.h" #ifdef GAME # define POOLSIZE ( 1024 * 1024 ) #else # define POOLSIZE ( 256 * 1024 ) #endif #define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value #define ROUNDBITS (unsigned int)31 // Round to 32 bytes typedef struct freeMemNode_s { // Size of ROUNDBITS int cookie, size; // Size includes node (obviously) struct freeMemNode_s *prev, *next; } freeMemNode_t; static char memoryPool[POOLSIZE]; static freeMemNode_t *freeHead; static int freeMem; /* * Returns qtrue if BG_Alloc will succeed, qfalse otherwise */ qboolean BG_CanAlloc( unsigned int size) { freeMemNode_t *fmn; int allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary for( fmn = freeHead; fmn; fmn = fmn->next ) { if( fmn->cookie != FREEMEMCOOKIE ) { //Memory curroption return qfalse; } if( fmn->size >= allocsize ) { //At least one useable block return qtrue; } } return qfalse; } void *BG_Alloc( unsigned int size ) { // Find a free block and allocate. // Does two passes, attempts to fill same-sized free slot first. freeMemNode_t *fmn, *prev, *next, *smallest; int allocsize, smallestsize; char *endptr; int *ptr; allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary ptr = NULL; smallest = NULL; smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :) for( fmn = freeHead; fmn; fmn = fmn->next ) { if( fmn->cookie != FREEMEMCOOKIE ) Com_Error( ERR_DROP, "BG_Alloc: Memory corruption detected!\n" ); if( fmn->size >= allocsize ) { // We've got a block if( fmn->size == allocsize ) { // Same size, just remove prev = fmn->prev; next = fmn->next; if( prev ) prev->next = next; // Point previous node to next if( next ) next->prev = prev; // Point next node to previous if( fmn == freeHead ) freeHead = next; // Set head pointer to next ptr = (int *) fmn; break; // Stop the loop, this is fine } else { // Keep track of the smallest free slot if( fmn->size < smallestsize ) { smallest = fmn; smallestsize = fmn->size; } } } } if( !ptr && smallest ) { // We found a slot big enough smallest->size -= allocsize; endptr = (char *) smallest + smallest->size; ptr = (int *) endptr; } if( ptr ) { freeMem -= allocsize; memset( ptr, 0, allocsize ); *ptr++ = allocsize; // Store a copy of size for deallocation return( (void *) ptr ); } Com_Error( ERR_DROP, "BG_Alloc: failed on allocation of %i bytes\n", size ); return( NULL ); } void BG_Free( void *ptr ) { // Release allocated memory, add it to the free list. freeMemNode_t *fmn; char *freeend; int *freeptr; freeptr = ptr; freeptr--; freeMem += *freeptr; for( fmn = freeHead; fmn; fmn = fmn->next ) { freeend = ((char *) fmn) + fmn->size; if( freeend == (char *) freeptr ) { // Released block can be merged to an existing node fmn->size += *freeptr; // Add size of node. return; } } // No merging, add to head of list fmn = (freeMemNode_t *) freeptr; fmn->size = *freeptr; // Set this first to avoid corrupting *freeptr fmn->cookie = FREEMEMCOOKIE; fmn->prev = NULL; fmn->next = freeHead; freeHead->prev = fmn; freeHead = fmn; } void BG_InitMemory( void ) { // Set up the initial node freeHead = (freeMemNode_t *)memoryPool; freeHead->cookie = FREEMEMCOOKIE; freeHead->size = POOLSIZE; freeHead->next = NULL; freeHead->prev = NULL; freeMem = sizeof( memoryPool ); } void BG_DefragmentMemory( void ) { // If there's a frenzy of deallocation and we want to // allocate something big, this is useful. Otherwise... // not much use. freeMemNode_t *startfmn, *endfmn, *fmn; for( startfmn = freeHead; startfmn; ) { endfmn = (freeMemNode_t *)(((char *) startfmn) + startfmn->size); for( fmn = freeHead; fmn; ) { if( fmn->cookie != FREEMEMCOOKIE ) Com_Error( ERR_DROP, "BG_DefragmentMemory: Memory corruption detected!\n" ); if( fmn == endfmn ) { // We can add fmn onto startfmn. if( fmn->prev ) fmn->prev->next = fmn->next; if( fmn->next ) { if( !(fmn->next->prev = fmn->prev) ) freeHead = fmn->next; // We're removing the head node } startfmn->size += fmn->size; memset( fmn, 0, sizeof(freeMemNode_t) ); // A redundant call, really. startfmn = freeHead; endfmn = fmn = NULL; // Break out of current loop } else fmn = fmn->next; } if( endfmn ) startfmn = startfmn->next; // endfmn acts as a 'restart' flag here } } //KK-OAX This was moved from g_mem.c to keep functionality from being broken. void Svcmd_GameMem_f( void ) { int usedMem; usedMem = POOLSIZE - freeMem; G_Printf( "Game memory status: %i out of %i bytes allocated\n", usedMem, POOLSIZE ); } openarena_0.8.8.orig/code/game/g_admin.h0000644000175000017500000001552711656310264016632 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2004-2006 Tony J. White Portions Copyright (C) 2009 Karl F. Kuglin This file is part of the Open Arena source code. Originally copied from Tremulous under GPL version 2 including any later version. The code has been modified to fit the needs of Open Arena. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef _G_ADMIN_H #define _G_ADMIN_H #define AP(x) trap_SendServerCommand(-1, x) #define CP(x) trap_SendServerCommand(ent-g_entities, x) #define CPx(x, y) trap_SendServerCommand(x, y) #define ADMP(x) G_admin_print(ent, x) #define ADMBP(x) G_admin_buffer_print(ent, x) #define ADMBP_begin() G_admin_buffer_begin() #define ADMBP_end() G_admin_buffer_end(ent) #define MAX_ADMIN_LEVELS 32 #define MAX_ADMIN_ADMINS 1024 #define MAX_ADMIN_BANS 1024 #define MAX_ADMIN_NAMELOGS 128 #define MAX_ADMIN_NAMELOG_NAMES 5 #define MAX_ADMIN_FLAGS 64 #define MAX_ADMIN_COMMANDS 64 #define MAX_ADMIN_CMD_LEN 20 #define MAX_ADMIN_BAN_REASON 50 //KK-OAX #define MAX_ADMIN_WARNINGS 1024 /* * 1 - cannot be vote kicked, vote muted * 2 - cannot be censored or flood protected TODO * 3 - never loses credits for changing teams * 4 - can see team chat as a spectator * 5 - can switch teams any time, regardless of balance * 6 - does not need to specify a reason for a kick/ban * 7 - can call a vote at any time (regardless of a vote being disabled or * voting limitations) * 8 - does not need to specify a duration for a ban * 9 - can run commands from team chat * 0 - inactivity rules do not apply to them * ! - admin commands cannot be used on them * @ - does not show up as an admin in !listplayers * $ - sees all information in !listplayers * ? - receieves and can send /a admin messages */ #define ADMF_IMMUNITY '1' #define ADMF_NOCENSORFLOOD '2' /* TODO */ #define ADMF_TEAMCHANGEFREE '3' #define ADMF_SPEC_ALLCHAT '4' #define ADMF_FORCETEAMCHANGE '5' #define ADMF_UNACCOUNTABLE '6' #define ADMF_NO_VOTE_LIMIT '7' #define ADMF_CAN_PERM_BAN '8' #define ADMF_TEAMCHAT_CMD '9' #define ADMF_ACTIVITY '0' #define ADMF_IMMUTABLE '!' #define ADMF_INCOGNITO '@' #define ADMF_ADMINCHAT '?' #define MAX_ADMIN_LISTITEMS 20 #define MAX_ADMIN_SHOWBANS 10 // important note: QVM does not seem to allow a single char to be a // member of a struct at init time. flag has been converted to char* typedef struct { char *keyword; qboolean ( * handler ) ( gentity_t *ent, int skiparg ); char *flag; char *function; // used for !help char *syntax; // used for !help } g_admin_cmd_t; typedef struct g_admin_level { int level; char name[ MAX_NAME_LENGTH ]; char flags[ MAX_ADMIN_FLAGS ]; } g_admin_level_t; typedef struct g_admin_admin { char guid[ 33 ]; char name[ MAX_NAME_LENGTH ]; int level; char flags[ MAX_ADMIN_FLAGS ]; } g_admin_admin_t; typedef struct g_admin_ban { char name[ MAX_NAME_LENGTH ]; char guid[ 33 ]; char ip[ 40 ]; char reason[ MAX_ADMIN_BAN_REASON ]; char made[ 18 ]; // big enough for strftime() %c int expires; char banner[ MAX_NAME_LENGTH ]; } g_admin_ban_t; typedef struct g_admin_command { char command[ MAX_ADMIN_CMD_LEN ]; char exec[ MAX_QPATH ]; char desc[ 50 ]; int levels[ MAX_ADMIN_LEVELS + 1 ]; } g_admin_command_t; typedef struct g_admin_namelog { char name[ MAX_ADMIN_NAMELOG_NAMES ][ MAX_NAME_LENGTH ]; char ip[ 40 ]; char guid[ 33 ]; int slot; qboolean banned; } g_admin_namelog_t; //KK-OAX Added for Warnings typedef struct g_admin_warning { char name[ MAX_NAME_LENGTH ]; char guid[ 33 ]; char ip[ 40 ]; char warning[MAX_STRING_CHARS]; char made[ 18 ]; char warner[MAX_NAME_LENGTH]; int expires; } g_admin_warning_t; qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ); qboolean G_admin_cmd_check( gentity_t *ent, qboolean say ); qboolean G_admin_readconfig( gentity_t *ent, int skiparg ); qboolean G_admin_permission( gentity_t *ent, char flag ); qboolean G_admin_name_check( gentity_t *ent, char *name, char *err, int len ); void G_admin_namelog_update( gclient_t *ent, qboolean disconnect ); int G_admin_level( gentity_t *ent ); int G_admin_parse_time( const char *time ); // ! command functions qboolean G_admin_time( gentity_t *ent, int skiparg ); qboolean G_admin_setlevel( gentity_t *ent, int skiparg ); qboolean G_admin_kick( gentity_t *ent, int skiparg ); qboolean G_admin_adjustban( gentity_t *ent, int skiparg ); qboolean G_admin_ban( gentity_t *ent, int skiparg ); qboolean G_admin_unban( gentity_t *ent, int skiparg ); qboolean G_admin_putteam( gentity_t *ent, int skiparg ); qboolean G_admin_listadmins( gentity_t *ent, int skiparg ); qboolean G_admin_listplayers( gentity_t *ent, int skiparg ); qboolean G_admin_map( gentity_t *ent, int skiparg ); qboolean G_admin_mute( gentity_t *ent, int skiparg ); qboolean G_admin_showbans( gentity_t *ent, int skiparg ); qboolean G_admin_help( gentity_t *ent, int skiparg ); qboolean G_admin_admintest( gentity_t *ent, int skiparg ); qboolean G_admin_allready( gentity_t *ent, int skiparg ); qboolean G_admin_cancelvote( gentity_t *ent, int skiparg ); qboolean G_admin_passvote( gentity_t *ent, int skiparg ); qboolean G_admin_spec999( gentity_t *ent, int skiparg ); qboolean G_admin_rename( gentity_t *ent, int skiparg ); qboolean G_admin_restart( gentity_t *ent, int skiparg ); qboolean G_admin_nextmap( gentity_t *ent, int skiparg ); qboolean G_admin_namelog( gentity_t *ent, int skiparg ); qboolean G_admin_lock( gentity_t *ent, int skiparg ); qboolean G_admin_unlock( gentity_t *ent, int skiparg ); //KK-OAX qboolean G_admin_disorient( gentity_t *ent, int skiparg ); qboolean G_admin_orient(gentity_t *ent, int skiparg ); qboolean G_admin_slap(gentity_t *ent, int skiparg ); qboolean G_admin_warn( gentity_t *ent, int skiparg ); qboolean G_admin_shuffle( gentity_t *ent, int skiparg ); void G_admin_print( gentity_t *ent, char *m ); void G_admin_buffer_print( gentity_t *ent, char *m ); void G_admin_buffer_begin( void ); void G_admin_buffer_end( gentity_t *ent ); void G_admin_duration( int secs, char *duration, int dursize ); void G_admin_cleanup( void ); void G_admin_namelog_cleanup( void ); #endif /* ifndef _G_ADMIN_H */ openarena_0.8.8.orig/code/game/bg_public.h0000644000175000017500000005225211656310264017156 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_public.h -- definitions shared by both the server game and client game modules // because games can change separately from the main system version, we need a // second version that must match between game and cgame #if defined(BG_PUBLIC_H) #else #define BG_PUBLIC_H 1 #define GAME_VERSION BASEGAME "-1" #define DEFAULT_GRAVITY 800 #define GIB_HEALTH -40 #define ARMOR_PROTECTION 0.66 #define MAX_ITEMS 256 #define RANK_TIED_FLAG 0x4000 #define DEFAULT_SHOTGUN_SPREAD 700 #define DEFAULT_SHOTGUN_COUNT 11 #define ITEM_RADIUS 15 // item sizes are needed for client side pickup detection #define LIGHTNING_RANGE 768 #define SCORE_NOT_PRESENT -9999 // for the CS_SCORES[12] when only one player is present #define VOTE_TIME 30000 // 30 seconds before vote times out #define MINS_Z -24 #define DEFAULT_VIEWHEIGHT 26 #define CROUCH_VIEWHEIGHT 12 #define DEAD_VIEWHEIGHT -16 //Domination points #define MAX_DOMINATION_POINTS 6 #define MAX_DOMINATION_POINTS_NAMES 20 // // config strings are a general means of communicating variable length strings // from the server to all connected clients. // // CS_SERVERINFO and CS_SYSTEMINFO are defined in q_shared.h #define CS_MUSIC 2 #define CS_MESSAGE 3 // from the map worldspawn's message field #define CS_MOTD 4 // g_motd string for server message of the day #define CS_WARMUP 5 // server time when the match will be restarted #define CS_SCORES1 6 #define CS_SCORES2 7 #define CS_VOTE_TIME 8 #define CS_VOTE_STRING 9 #define CS_VOTE_YES 10 #define CS_VOTE_NO 11 #define CS_TEAMVOTE_TIME 12 #define CS_TEAMVOTE_STRING 14 #define CS_TEAMVOTE_YES 16 #define CS_TEAMVOTE_NO 18 #define CS_GAME_VERSION 20 #define CS_LEVEL_START_TIME 21 // so the timer only shows the current level #define CS_INTERMISSION 22 // when 1, fraglimit/timelimit has been hit and intermission will start in a second or two #define CS_FLAGSTATUS 23 // string indicating flag status in CTF #define CS_SHADERSTATE 24 #define CS_BOTINFO 25 #define CS_ITEMS 27 // string of 0's and 1's that tell which items are present #define CS_MODELS 32 #define CS_SOUNDS (CS_MODELS+MAX_MODELS) #define CS_PLAYERS (CS_SOUNDS+MAX_SOUNDS) #define CS_LOCATIONS (CS_PLAYERS+MAX_CLIENTS) #define CS_PARTICLES (CS_LOCATIONS+MAX_LOCATIONS) #define CS_MAX (CS_PARTICLES+MAX_LOCATIONS) #if (CS_MAX) > MAX_CONFIGSTRINGS #error overflow: (CS_MAX) > MAX_CONFIGSTRINGS #endif typedef enum { GT_FFA, // free for all GT_TOURNAMENT, // one on one tournament GT_SINGLE_PLAYER, // single player ffa //-- team games go after this -- GT_TEAM, // team deathmatch //-- team games that uses bases go after this GT_CTF, // capture the flag GT_1FCTF, GT_OBELISK, GT_HARVESTER, //-- custom game types, there will be a variable in GT_ELIMINATION, // team elimination (custom) GT_CTF_ELIMINATION, // ctf elimination GT_LMS, // Last man standing GT_DOUBLE_D, // Double Domination GT_DOMINATION, // Standard domination 12 GT_MAX_GAME_TYPE } gametype_t; typedef enum { GENDER_MALE, GENDER_FEMALE, GENDER_NEUTER } gender_t; /* =================================================================================== PMOVE MODULE The pmove code takes a player_state_t and a usercmd_t and generates a new player_state_t and some other output data. Used for local prediction on the client game and true movement on the server game. =================================================================================== */ typedef enum { PM_NORMAL, // can accelerate and turn PM_NOCLIP, // noclip movement PM_SPECTATOR, // still run into walls PM_DEAD, // no acceleration or turning, but free falling PM_FREEZE, // stuck in place with no control PM_INTERMISSION, // no movement or status bar PM_SPINTERMISSION // no movement or status bar } pmtype_t; typedef enum { WEAPON_READY, WEAPON_RAISING, WEAPON_DROPPING, WEAPON_FIRING } weaponstate_t; // pmove->pm_flags #define PMF_DUCKED 1 #define PMF_JUMP_HELD 2 #define PMF_BACKWARDS_JUMP 8 // go into backwards land #define PMF_BACKWARDS_RUN 16 // coast down to backwards run #define PMF_TIME_LAND 32 // pm_time is time before rejump #define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time #define PMF_TIME_WATERJUMP 256 // pm_time is waterjump #define PMF_RESPAWNED 512 // clear after attack and jump buttons come up #define PMF_USE_ITEM_HELD 1024 #define PMF_GRAPPLE_PULL 2048 // pull towards grapple location #define PMF_FOLLOW 4096 // spectate following another player #define PMF_SCOREBOARD 8192 // spectate as a scoreboard #define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size //Elimination players cannot fire in warmup #define PMF_ELIMWARMUP 32768 //I hope this is more than 16 signed bits! (it's not but it just works anyway...) //Don't add anymore, I have already set the sign bit :-( #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK) #define MAXTOUCH 32 typedef struct { // state (in / out) playerState_t *ps; // command (in) usercmd_t cmd; int tracemask; // collide against these types of surfaces int debugLevel; // if set, diagnostic output will be printed qboolean noFootsteps; // if the game is setup for no footsteps by the server qboolean gauntletHit; // true if a gauntlet attack would actually hit something int framecount; // results (out) int numtouch; int touchents[MAXTOUCH]; vec3_t mins, maxs; // bounding box size int watertype; int waterlevel; float xyspeed; // for fixed msec Pmove int pmove_fixed; int pmove_msec; //Sago's pmove int pmove_float; //Flags effecting movement (see dmflags) int pmove_flags; // callbacks to test the world // these will be different functions during game and cgame void (*trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ); int (*pointcontents)( const vec3_t point, int passEntityNum ); } pmove_t; // if a full pmove isn't done on the client, you can just update the angles void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ); void Pmove (pmove_t *pmove); //=================================================================================== // player_state->stats[] indexes // NOTE: may not have more than 16 typedef enum { STAT_HEALTH, STAT_HOLDABLE_ITEM, STAT_PERSISTANT_POWERUP, STAT_WEAPONS, // 16 bit fields STAT_ARMOR, STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) STAT_MAX_HEALTH // health / armor limit, changable by handicap } statIndex_t; // player_state->persistant[] indexes // these fields are the only part of player_state that isn't // cleared on respawn // NOTE: may not have more than 16 typedef enum { PERS_SCORE, // !!! MUST NOT CHANGE, SERVER AND GAME BOTH REFERENCE !!! PERS_HITS, // total points damage inflicted so damage beeps can sound on change PERS_RANK, // player rank or team rank PERS_TEAM, // player team PERS_SPAWN_COUNT, // incremented every respawn PERS_PLAYEREVENTS, // 16 bits that can be flipped for events PERS_ATTACKER, // clientnum of last damage inflicter PERS_ATTACKEE_ARMOR, // health/armor of last person we attacked PERS_KILLED, // count of the number of times you died // player awards tracking PERS_IMPRESSIVE_COUNT, // two railgun hits in a row PERS_EXCELLENT_COUNT, // two successive kills in a short amount of time PERS_DEFEND_COUNT, // defend awards PERS_ASSIST_COUNT, // assist awards PERS_GAUNTLET_FRAG_COUNT, // kills with the guantlet PERS_CAPTURES // captures } persEnum_t; // entityState_t->eFlags #define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD #define EF_TICKING 0x00000002 // used to make players play the prox mine ticking sound #define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes #define EF_AWARD_EXCELLENT 0x00000008 // draw an excellent sprite #define EF_PLAYER_EVENT 0x00000010 #define EF_BOUNCE 0x00000010 // for missiles #define EF_BOUNCE_HALF 0x00000020 // for missiles #define EF_AWARD_GAUNTLET 0x00000040 // draw a gauntlet sprite #define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) #define EF_FIRING 0x00000100 // for lightning gun #define EF_KAMIKAZE 0x00000200 #define EF_MOVER_STOP 0x00000400 // will push otherwise #define EF_AWARD_CAP 0x00000800 // draw the capture sprite #define EF_TALK 0x00001000 // draw a talk balloon #define EF_CONNECTION 0x00002000 // draw a connection trouble sprite #define EF_VOTED 0x00004000 // already cast a vote #define EF_AWARD_IMPRESSIVE 0x00008000 // draw an impressive sprite #define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite #define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite #define EF_AWARD_DENIED 0x00040000 // denied #define EF_TEAMVOTED 0x00080000 // already cast a team vote // NOTE: may not have more than 16 typedef enum { PW_NONE, PW_QUAD, PW_BATTLESUIT, PW_HASTE, PW_INVIS, PW_REGEN, PW_FLIGHT, PW_REDFLAG, PW_BLUEFLAG, PW_NEUTRALFLAG, PW_SCOUT, PW_GUARD, PW_DOUBLER, PW_AMMOREGEN, PW_INVULNERABILITY, PW_NUM_POWERUPS } powerup_t; typedef enum { HI_NONE, HI_TELEPORTER, HI_MEDKIT, HI_KAMIKAZE, HI_PORTAL, HI_INVULNERABILITY, HI_NUM_HOLDABLE } holdable_t; typedef enum { DD_NONE, DD_POINTARED, DD_POINTABLUE, DD_POINTAWHITE, DD_POINTBRED, DD_POINTBBLUE, DD_POINTBWHITE } doubled_t; typedef enum { DOM_NONE, DOM_POINTRED, DOM_POINTBLUE, DOM_POINTWHITE, } domination_t; typedef enum { WP_NONE, WP_GAUNTLET, WP_MACHINEGUN, WP_SHOTGUN, WP_GRENADE_LAUNCHER, WP_ROCKET_LAUNCHER, WP_LIGHTNING, WP_RAILGUN, WP_PLASMAGUN, WP_BFG, WP_GRAPPLING_HOOK, WP_NAILGUN, WP_PROX_LAUNCHER, WP_CHAINGUN, WP_NUM_WEAPONS } weapon_t; // reward sounds (stored in ps->persistant[PERS_PLAYEREVENTS]) #define PLAYEREVENT_DENIEDREWARD 0x0001 #define PLAYEREVENT_GAUNTLETREWARD 0x0002 #define PLAYEREVENT_HOLYSHIT 0x0004 // entityState_t->event values // entity events are for effects that take place reletive // to an existing entities origin. Very network efficient. // two bits at the top of the entityState->event field // will be incremented with each change in the event so // that an identical event started twice in a row can // be distinguished. And off the value with ~EV_EVENT_BITS // to retrieve the actual event number #define EV_EVENT_BIT1 0x00000100 #define EV_EVENT_BIT2 0x00000200 #define EV_EVENT_BITS (EV_EVENT_BIT1|EV_EVENT_BIT2) #define EVENT_VALID_MSEC 300 typedef enum { EV_NONE, EV_FOOTSTEP, EV_FOOTSTEP_METAL, EV_FOOTSPLASH, EV_FOOTWADE, EV_SWIM, EV_STEP_4, EV_STEP_8, EV_STEP_12, EV_STEP_16, EV_FALL_SHORT, EV_FALL_MEDIUM, EV_FALL_FAR, EV_JUMP_PAD, // boing sound at origin, jump sound on player EV_JUMP, //Event 14 EV_WATER_TOUCH, // foot touches EV_WATER_LEAVE, // foot leaves EV_WATER_UNDER, // head touches EV_WATER_CLEAR, // head leaves EV_ITEM_PICKUP, // normal item pickups are predictable EV_GLOBAL_ITEM_PICKUP, // powerup / team sounds are broadcast to everyone EV_NOAMMO, EV_CHANGE_WEAPON, EV_FIRE_WEAPON, EV_USE_ITEM0, //Event 24 EV_USE_ITEM1, EV_USE_ITEM2, EV_USE_ITEM3, EV_USE_ITEM4, EV_USE_ITEM5, EV_USE_ITEM6, EV_USE_ITEM7, EV_USE_ITEM8, EV_USE_ITEM9, EV_USE_ITEM10, EV_USE_ITEM11, EV_USE_ITEM12, EV_USE_ITEM13, EV_USE_ITEM14, EV_USE_ITEM15, EV_ITEM_RESPAWN, //Event 40 EV_ITEM_POP, EV_PLAYER_TELEPORT_IN, EV_PLAYER_TELEPORT_OUT, EV_GRENADE_BOUNCE, // eventParm will be the soundindex EV_GENERAL_SOUND, EV_GLOBAL_SOUND, // no attenuation EV_GLOBAL_TEAM_SOUND, EV_BULLET_HIT_FLESH, EV_BULLET_HIT_WALL, EV_MISSILE_HIT, //Event 50 EV_MISSILE_MISS, EV_MISSILE_MISS_METAL, EV_RAILTRAIL, EV_SHOTGUN, EV_BULLET, // otherEntity is the shooter EV_PAIN, EV_DEATH1, EV_DEATH2, EV_DEATH3, EV_OBITUARY, //Event 60 EV_POWERUP_QUAD, EV_POWERUP_BATTLESUIT, EV_POWERUP_REGEN, EV_GIB_PLAYER, // gib a previously living player EV_SCOREPLUM, // score plum EV_PROXIMITY_MINE_STICK, EV_PROXIMITY_MINE_TRIGGER, EV_KAMIKAZE, // kamikaze explodes EV_OBELISKEXPLODE, // obelisk explodes EV_OBELISKPAIN, // obelisk is in pain EV_INVUL_IMPACT, // invulnerability sphere impact EV_JUICED, // invulnerability juiced effect EV_LIGHTNINGBOLT, // lightning bolt bounced of invulnerability sphere EV_DEBUG_LINE, EV_STOPLOOPINGSOUND, EV_TAUNT, EV_TAUNT_YES, EV_TAUNT_NO, EV_TAUNT_FOLLOWME, EV_TAUNT_GETFLAG, EV_TAUNT_GUARDBASE, EV_TAUNT_PATROL } entity_event_t; typedef enum { GTS_RED_CAPTURE, GTS_BLUE_CAPTURE, GTS_RED_RETURN, GTS_BLUE_RETURN, GTS_RED_TAKEN, GTS_BLUE_TAKEN, GTS_REDOBELISK_ATTACKED, GTS_BLUEOBELISK_ATTACKED, GTS_REDTEAM_SCORED, GTS_BLUETEAM_SCORED, GTS_REDTEAM_TOOK_LEAD, GTS_BLUETEAM_TOOK_LEAD, GTS_TEAMS_ARE_TIED, GTS_KAMIKAZE } global_team_sound_t; // animations typedef enum { BOTH_DEATH1, BOTH_DEAD1, BOTH_DEATH2, BOTH_DEAD2, BOTH_DEATH3, BOTH_DEAD3, TORSO_GESTURE, TORSO_ATTACK, TORSO_ATTACK2, TORSO_DROP, TORSO_RAISE, TORSO_STAND, TORSO_STAND2, LEGS_WALKCR, LEGS_WALK, LEGS_RUN, LEGS_BACK, LEGS_SWIM, LEGS_JUMP, LEGS_LAND, LEGS_JUMPB, LEGS_LANDB, LEGS_IDLE, LEGS_IDLECR, LEGS_TURN, TORSO_GETFLAG, TORSO_GUARDBASE, TORSO_PATROL, TORSO_FOLLOWME, TORSO_AFFIRMATIVE, TORSO_NEGATIVE, // BOTH_POSE, // leilei - crappy ui posing code trying MAX_ANIMATIONS, LEGS_BACKCR, LEGS_BACKWALK, FLAG_RUN, FLAG_STAND, FLAG_STAND2RUN, MAX_TOTALANIMATIONS } animNumber_t; typedef struct animation_s { int firstFrame; int numFrames; int loopFrames; // 0 to numFrames int frameLerp; // msec between frames int initialLerp; // msec to get to first frame int reversed; // true if animation is reversed int flipflop; // true if animation should flipflop back to base } animation_t; // flip the togglebit every time an animation // changes so a restart of the same anim can be detected #define ANIM_TOGGLEBIT 128 typedef enum { TEAM_FREE, TEAM_RED, TEAM_BLUE, TEAM_SPECTATOR, TEAM_NUM_TEAMS } team_t; // This is a fair assumption for Double Domination: #define TEAM_NONE TEAM_SPECTATOR // Time between location updates #define TEAM_LOCATION_UPDATE_TIME 1000 // How many players on the overlay #define TEAM_MAXOVERLAY 32 //team task typedef enum { TEAMTASK_NONE, TEAMTASK_OFFENSE, TEAMTASK_DEFENSE, TEAMTASK_PATROL, TEAMTASK_FOLLOW, TEAMTASK_RETRIEVE, TEAMTASK_ESCORT, TEAMTASK_CAMP } teamtask_t; // means of death typedef enum { MOD_UNKNOWN, MOD_SHOTGUN, MOD_GAUNTLET, MOD_MACHINEGUN, MOD_GRENADE, MOD_GRENADE_SPLASH, MOD_ROCKET, MOD_ROCKET_SPLASH, MOD_PLASMA, MOD_PLASMA_SPLASH, MOD_RAILGUN, MOD_LIGHTNING, MOD_BFG, MOD_BFG_SPLASH, MOD_WATER, MOD_SLIME, MOD_LAVA, MOD_CRUSH, MOD_TELEFRAG, MOD_FALLING, MOD_SUICIDE, MOD_TARGET_LASER, MOD_TRIGGER_HURT, MOD_NAIL, MOD_CHAINGUN, MOD_PROXIMITY_MINE, MOD_KAMIKAZE, MOD_JUICED, MOD_GRAPPLE } meansOfDeath_t; //--------------------------------------------------------- // gitem_t->type typedef enum { IT_BAD, IT_WEAPON, // EFX: rotate + upscale + minlight IT_AMMO, // EFX: rotate IT_ARMOR, // EFX: rotate + minlight IT_HEALTH, // EFX: static external sphere + rotating internal IT_POWERUP, // instant on, timer based // EFX: rotate + external ring that rotates IT_HOLDABLE, // single use, holdable item // EFX: rotate + bob IT_PERSISTANT_POWERUP, IT_TEAM } itemType_t; #define MAX_ITEM_MODELS 4 typedef struct gitem_s { char *classname; // spawning name char *pickup_sound; char *world_model[MAX_ITEM_MODELS]; char *icon; char *pickup_name; // for printing on pickup int quantity; // for ammo how much, or duration of powerup itemType_t giType; // IT_* flags int giTag; char *precaches; // string of all models and images this item will use char *sounds; // string of all sounds this item will use } gitem_t; // included in both the game dll and the client extern gitem_t bg_itemlist[]; extern int bg_numItems; gitem_t *BG_FindItem( const char *pickupName ); gitem_t *BG_FindItemForWeapon( weapon_t weapon ); gitem_t *BG_FindItemForPowerup( powerup_t pw ); gitem_t *BG_FindItemForHoldable( holdable_t pw ); #define ITEM_INDEX(x) ((x)-bg_itemlist) qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ); // g_dmflags->integer flags #define DF_NO_FALLING 8 #define DF_FIXED_FOV 16 #define DF_NO_FOOTSTEPS 32 #define DF_INSTANT_WEAPON_CHANGE 64 #define DF_NO_BUNNY 128 #define DF_INVIS 256 #define DF_LIGHT_VOTING 512 #define DF_NO_SELF_DAMAGE 1024 #define DF_PLAYER_OVERLAY 2048 //g_videoflags->integer #define VF_LOCK_CVARS_BASIC 1 #define VF_LOCK_CVARS_EXTENDED 2 #define VF_LOCK_VERTEX 4 // g_elimflags->integer //This is used to signal the client that it cannot go to free spectator: #define EF_ONEWAY 1 #define EF_NO_FREESPEC 2 //g_voteflags->integer //Autoparsed from allowedvote //List: "/map_restart/nextmap/map/g_gametype/kick/clientkick/g_doWarmup/timelimit/fraglimit/custom/shuffle/" #define VF_map_restart 1 #define VF_nextmap 2 #define VF_map 4 #define VF_g_gametype 8 //Note that we skipped kick... not needed #define VF_clientkick 16 #define VF_g_doWarmup 32 #define VF_timelimit 64 #define VF_fraglimit 128 #define VF_custom 256 #define VF_shuffle 512 // content masks #define MASK_ALL (-1) #define MASK_SOLID (CONTENTS_SOLID) #define MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) #define MASK_DEADSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP) #define MASK_WATER (CONTENTS_WATER|CONTENTS_LAVA|CONTENTS_SLIME) #define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_SLIME|CONTENTS_LAVA) #define MASK_SHOT (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE) // // entityState_t->eType // typedef enum { ET_GENERAL, ET_PLAYER, ET_ITEM, ET_MISSILE, ET_MOVER, ET_BEAM, ET_PORTAL, ET_SPEAKER, ET_PUSH_TRIGGER, ET_TELEPORT_TRIGGER, ET_INVISIBLE, ET_GRAPPLE, // grapple hooked on wall ET_TEAM, ET_EVENTS // any of the EV_* events can be added freestanding // by setting eType to ET_EVENTS + eventNum // this avoids having to set eFlags and eventNum } entityType_t; //KK-OAX Using this now instead of g_mem.c // bg_alloc.c // qboolean BG_CanAlloc( unsigned int size ); void *BG_Alloc( unsigned int size ); void BG_InitMemory( void ); void BG_Free( void *ptr ); void BG_DefragmentMemory( void ); void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ); void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ); void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ); void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ); void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ); void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ); qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); #define ARENAS_PER_TIER 4 #define MAX_ARENAS 1024 #define MAX_ARENAS_TEXT 8192 #define MAX_BOTS 1024 #define MAX_BOTS_TEXT 8192 // Kamikaze // 1st shockwave times #define KAMI_SHOCKWAVE_STARTTIME 0 #define KAMI_SHOCKWAVEFADE_STARTTIME 1500 #define KAMI_SHOCKWAVE_ENDTIME 2000 // explosion/implosion times #define KAMI_EXPLODE_STARTTIME 250 #define KAMI_IMPLODE_STARTTIME 2000 #define KAMI_IMPLODE_ENDTIME 2250 // 2nd shockwave times #define KAMI_SHOCKWAVE2_STARTTIME 2000 #define KAMI_SHOCKWAVE2FADE_STARTTIME 2500 #define KAMI_SHOCKWAVE2_ENDTIME 3000 // radius of the models without scaling #define KAMI_SHOCKWAVEMODEL_RADIUS 88 #define KAMI_BOOMSPHEREMODEL_RADIUS 72 // maximum radius of the models during the effect #define KAMI_SHOCKWAVE_MAXRADIUS 1320 #define KAMI_BOOMSPHERE_MAXRADIUS 720 #define KAMI_SHOCKWAVE2_MAXRADIUS 704 //KK-OAX //bg_misc.c char *BG_TeamName( team_t team ); #endif openarena_0.8.8.orig/code/game/bg_pmove.c0000644000175000017500000013203611656310264017020 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_pmove.c -- both games player movement code // takes a playerstate and a usercmd as input and returns a modifed playerstate #include "../qcommon/q_shared.h" #include "bg_public.h" #include "bg_local.h" pmove_t *pm; pml_t pml; // movement parameters float pm_stopspeed = 100.0f; float pm_duckScale = 0.25f; float pm_swimScale = 0.50f; float pm_wadeScale = 0.70f; float pm_accelerate = 10.0f; float pm_airaccelerate = 1.0f; float pm_wateraccelerate = 4.0f; float pm_flyaccelerate = 8.0f; float pm_friction = 6.0f; float pm_waterfriction = 1.0f; float pm_flightfriction = 3.0f; float pm_spectatorfriction = 5.0f; int c_pmove = 0; /* =============== PM_AddEvent =============== */ void PM_AddEvent( int newEvent ) { BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps ); } /* =============== PM_AddTouchEnt =============== */ void PM_AddTouchEnt( int entityNum ) { int i; if ( entityNum == ENTITYNUM_WORLD ) { return; } if ( pm->numtouch == MAXTOUCH ) { return; } // see if it is already added for ( i = 0 ; i < pm->numtouch ; i++ ) { if ( pm->touchents[ i ] == entityNum ) { return; } } // add it pm->touchents[pm->numtouch] = entityNum; pm->numtouch++; } /* =================== PM_StartTorsoAnim =================== */ static void PM_StartTorsoAnim( int anim ) { if ( pm->ps->pm_type >= PM_DEAD ) { return; } pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } static void PM_StartLegsAnim( int anim ) { if ( pm->ps->pm_type >= PM_DEAD ) { return; } if ( pm->ps->legsTimer > 0 ) { return; // a high priority animation is running } pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } static void PM_ContinueLegsAnim( int anim ) { if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) { return; } if ( pm->ps->legsTimer > 0 ) { return; // a high priority animation is running } PM_StartLegsAnim( anim ); } static void PM_ContinueTorsoAnim( int anim ) { if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) { return; } if ( pm->ps->torsoTimer > 0 ) { return; // a high priority animation is running } PM_StartTorsoAnim( anim ); } static void PM_ForceLegsAnim( int anim ) { pm->ps->legsTimer = 0; PM_StartLegsAnim( anim ); } /* ================== PM_ClipVelocity Slide off of the impacting surface ================== */ void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { float backoff; float change; int i; backoff = DotProduct (in, normal); if ( backoff < 0 ) { backoff *= overbounce; } else { backoff /= overbounce; } for ( i=0 ; i<3 ; i++ ) { change = normal[i]*backoff; out[i] = in[i] - change; } } /* ================== PM_Friction Handles both ground friction and water friction ================== */ static void PM_Friction( void ) { vec3_t vec; float *vel; float speed, newspeed, control; float drop; vel = pm->ps->velocity; VectorCopy( vel, vec ); if ( pml.walking ) { vec[2] = 0; // ignore slope movement } speed = VectorLength(vec); if (speed < 1) { vel[0] = 0; vel[1] = 0; // allow sinking underwater // FIXME: still have z friction underwater? return; } drop = 0; // apply ground friction if ( pm->waterlevel <= 1 ) { if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { // if getting knocked back, no friction if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*pm_friction*pml.frametime; } } } // apply water friction even if just wading if ( pm->waterlevel ) { drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime; } // apply flying friction if ( pm->ps->powerups[PW_FLIGHT]) { drop += speed*pm_flightfriction*pml.frametime; } if ( pm->ps->pm_type == PM_SPECTATOR) { drop += speed*pm_spectatorfriction*pml.frametime; } // scale the velocity newspeed = speed - drop; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== PM_Accelerate TODO: bunny hoping Handles user intended acceleration ============== */ static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { if(! (pm->pmove_flags & DF_NO_BUNNY) ) { //#if 1 // q2 style int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct (pm->ps->velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } accelspeed = accel*pml.frametime*wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i=0 ; i<3 ; i++) { pm->ps->velocity[i] += accelspeed*wishdir[i]; } } else { //#else // proper way (avoids strafe jump maxspeed bug), but feels bad vec3_t wishVelocity; vec3_t pushDir; float pushLen; float canPush; VectorScale( wishdir, wishspeed, wishVelocity ); VectorSubtract( wishVelocity, pm->ps->velocity, pushDir ); pushLen = VectorNormalize( pushDir ); canPush = accel*pml.frametime*wishspeed; if (canPush > pushLen) { canPush = pushLen; } VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity ); } //#endif } /* ============ PM_CmdScale Returns the scale factor to apply to cmd movements This allows the clients to use axial -127 to 127 values for all directions without getting a sqrt(2) distortion in speed. ============ */ static float PM_CmdScale( usercmd_t *cmd ) { int max; float total; float scale; max = abs( cmd->forwardmove ); if ( abs( cmd->rightmove ) > max ) { max = abs( cmd->rightmove ); } if ( abs( cmd->upmove ) > max ) { max = abs( cmd->upmove ); } if ( !max ) { return 0; } total = sqrt( cmd->forwardmove * cmd->forwardmove + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove ); scale = (float)pm->ps->speed * max / ( 127.0 * total ); return scale; } /* ================ PM_SetMovementDir Determine the rotation of the legs reletive to the facing dir ================ */ static void PM_SetMovementDir( void ) { if ( pm->cmd.forwardmove || pm->cmd.rightmove ) { if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 0; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 1; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) { pm->ps->movementDir = 2; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 3; } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 4; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 5; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) { pm->ps->movementDir = 6; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 7; } } else { // if they aren't actively going directly sideways, // change the animation to the diagonal so they // don't stop too crooked if ( pm->ps->movementDir == 2 ) { pm->ps->movementDir = 1; } else if ( pm->ps->movementDir == 6 ) { pm->ps->movementDir = 7; } } } /* ============= PM_CheckJump ============= */ static qboolean PM_CheckJump( void ) { if ( pm->ps->pm_flags & PMF_RESPAWNED ) { return qfalse; // don't allow jump until all buttons are up } if ( pm->cmd.upmove < 10 ) { // not holding jump return qfalse; } // must wait for jump to be released if ( pm->ps->pm_flags & PMF_JUMP_HELD ) { // clear upmove so cmdscale doesn't lower running speed pm->cmd.upmove = 0; return qfalse; } pml.groundPlane = qfalse; // jumping away pml.walking = qfalse; pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->velocity[2] = JUMP_VELOCITY; PM_AddEvent( EV_JUMP ); if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } return qtrue; } /* ============= PM_CheckWaterJump ============= */ static qboolean PM_CheckWaterJump( void ) { vec3_t spot; int cont; vec3_t flatforward; if (pm->ps->pm_time) { return qfalse; } // check for water jump if ( pm->waterlevel != 2 ) { return qfalse; } flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; VectorNormalize (flatforward); VectorMA (pm->ps->origin, 30, flatforward, spot); spot[2] += 4; cont = pm->pointcontents (spot, pm->ps->clientNum ); if ( !(cont & CONTENTS_SOLID) ) { return qfalse; } spot[2] += 16; cont = pm->pointcontents (spot, pm->ps->clientNum ); if ( cont & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) ) { return qfalse; } // jump out of water VectorScale (pml.forward, 200, pm->ps->velocity); pm->ps->velocity[2] = 350; pm->ps->pm_flags |= PMF_TIME_WATERJUMP; pm->ps->pm_time = 2000; return qtrue; } //============================================================================ /* =================== PM_WaterJumpMove Flying out of the water =================== */ static void PM_WaterJumpMove( void ) { // waterjump has no control, but falls PM_StepSlideMove( qtrue ); pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; if (pm->ps->velocity[2] < 0) { // cancel as soon as we are falling down again pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } } /* =================== PM_WaterMove =================== */ static void PM_WaterMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; float vel; if ( PM_CheckWaterJump() ) { PM_WaterJumpMove(); return; } #if 0 // jump = head for surface if ( pm->cmd.upmove >= 10 ) { if (pm->ps->velocity[2] > -300) { if ( pm->watertype == CONTENTS_WATER ) { pm->ps->velocity[2] = 100; } else if (pm->watertype == CONTENTS_SLIME) { pm->ps->velocity[2] = 80; } else { pm->ps->velocity[2] = 50; } } } #endif PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = -60; // sink towards bottom } else { for (i=0 ; i<3 ; i++) wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if ( wishspeed > pm->ps->speed * pm_swimScale ) { wishspeed = pm->ps->speed * pm_swimScale; } PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); // make sure we can go up slopes easily under water if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); } PM_SlideMove( qfalse ); } /* =================== PM_InvulnerabilityMove Only with the invulnerability powerup =================== */ static void PM_InvulnerabilityMove( void ) { pm->cmd.forwardmove = 0; pm->cmd.rightmove = 0; pm->cmd.upmove = 0; VectorClear(pm->ps->velocity); } /* =================== PM_FlyMove Only with the flight powerup =================== */ static void PM_FlyMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; // normal slowdown PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = 0; } else { for (i=0 ; i<3 ; i++) { wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; } wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); PM_StepSlideMove( qfalse ); } /* =================== PM_AirMove =================== */ static void PM_AirMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; PM_Friction(); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 2 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // not on ground, so little effect on velocity PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); // we may have a ground plane that is very steep, even // though we don't have a groundentity // slide along the steep plane if ( pml.groundPlane ) { PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } #if 0 //ZOID: If we are on the grapple, try stair-stepping //this allows a player to use the grapple to pull himself //over a ledge if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) PM_StepSlideMove ( qtrue ); else PM_SlideMove ( qtrue ); #endif PM_StepSlideMove ( qtrue ); } /* =================== PM_GrappleMove =================== */ static void PM_GrappleMove( void ) { vec3_t vel, v; float vlen; VectorScale(pml.forward, -16, v); VectorAdd(pm->ps->grapplePoint, v, v); VectorSubtract(v, pm->ps->origin, vel); vlen = VectorLength(vel); VectorNormalize( vel ); if (vlen <= 100) VectorScale(vel, 10 * vlen, vel); else VectorScale(vel, 800, vel); VectorCopy(vel, pm->ps->velocity); pml.groundPlane = qfalse; } /* =================== PM_WalkMove =================== */ static void PM_WalkMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; float accelerate; float vel; if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) { // begin swimming PM_WaterMove(); return; } if ( PM_CheckJump () ) { // jumped away if ( pm->waterlevel > 1 ) { PM_WaterMove(); } else { PM_AirMove(); } return; } PM_Friction (); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; // project the forward and right directions onto the ground plane PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP ); PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP ); // VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 3 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } // when going up or down slopes the wish velocity should Not be zero // wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // clamp the speed lower if ducking if ( pm->ps->pm_flags & PMF_DUCKED ) { if ( wishspeed > pm->ps->speed * pm_duckScale ) { wishspeed = pm->ps->speed * pm_duckScale; } } // clamp the speed lower if wading or walking on the bottom if ( pm->waterlevel ) { float waterScale; waterScale = pm->waterlevel / 3.0; waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale; if ( wishspeed > pm->ps->speed * waterScale ) { wishspeed = pm->ps->speed * waterScale; } } // when a player gets hit, they temporarily lose // full control, which allows them to be moved a bit if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { accelerate = pm_airaccelerate; } else { accelerate = pm_accelerate; } PM_Accelerate (wishdir, wishspeed, accelerate); //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; } else { // don't reset the z velocity for slopes // pm->ps->velocity[2] = 0; } vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); // don't decrease velocity when going up or down a slope VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); // don't do anything if standing still if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) { return; } PM_StepSlideMove( qfalse ); //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); } /* ============== PM_DeadMove ============== */ static void PM_DeadMove( void ) { float forward; if ( !pml.walking ) { return; } // extra friction forward = VectorLength (pm->ps->velocity); forward -= 20; if ( forward <= 0 ) { VectorClear (pm->ps->velocity); } else { VectorNormalize (pm->ps->velocity); VectorScale (pm->ps->velocity, forward, pm->ps->velocity); } } /* =============== PM_NoclipMove =============== */ static void PM_NoclipMove( void ) { float speed, drop, friction, control, newspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; pm->ps->viewheight = DEFAULT_VIEWHEIGHT; // friction speed = VectorLength (pm->ps->velocity); if (speed < 1) { VectorCopy (vec3_origin, pm->ps->velocity); } else { drop = 0; friction = pm_friction*1.5; // extra friction control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); } // accelerate scale = PM_CmdScale( &pm->cmd ); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; for (i=0 ; i<3 ; i++) wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; wishvel[2] += pm->cmd.upmove; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; PM_Accelerate( wishdir, wishspeed, pm_accelerate ); // move VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); } //============================================================================ /* ================ PM_FootstepForSurface Returns an event number apropriate for the groundsurface ================ */ static int PM_FootstepForSurface( void ) { if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) { return 0; } if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) { return EV_FOOTSTEP_METAL; } return EV_FOOTSTEP; } /* ================= PM_CrashLand Check for hard landings that generate sound events ================= */ static void PM_CrashLand( void ) { float delta; float dist; float vel, acc; float t; float a, b, c, den; // decide which landing animation to use if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) { PM_ForceLegsAnim( LEGS_LANDB ); } else { PM_ForceLegsAnim( LEGS_LAND ); } pm->ps->legsTimer = TIMER_LAND; // calculate the exact velocity on landing dist = pm->ps->origin[2] - pml.previous_origin[2]; vel = pml.previous_velocity[2]; acc = -pm->ps->gravity; a = acc / 2; b = vel; c = -dist; den = b * b - 4 * a * c; if ( den < 0 ) { return; } t = (-b - sqrt( den ) ) / ( 2 * a ); delta = vel + t * acc; delta = delta*delta * 0.0001; // ducking while falling doubles damage if ( pm->ps->pm_flags & PMF_DUCKED ) { delta *= 2; } // never take falling damage if completely underwater if ( pm->waterlevel == 3 ) { return; } // reduce falling damage if there is standing water if ( pm->waterlevel == 2 ) { delta *= 0.25; } if ( pm->waterlevel == 1 ) { delta *= 0.5; } if ( delta < 1 ) { return; } // create a local entity event to play the sound // SURF_NODAMAGE is used for bounce pads where you don't ever // want to take damage or play a crunch sound if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) { if ( delta > 60 ) { PM_AddEvent( EV_FALL_FAR ); } else if ( delta > 40 ) { // this is a pain grunt, so don't play it if dead if ( pm->ps->stats[STAT_HEALTH] > 0 ) { PM_AddEvent( EV_FALL_MEDIUM ); } } else if ( delta > 7 ) { PM_AddEvent( EV_FALL_SHORT ); } else { PM_AddEvent( PM_FootstepForSurface() ); } } // start footstep cycle over pm->ps->bobCycle = 0; } /* ============= PM_CheckStuck ============= */ /* void PM_CheckStuck(void) { trace_t trace; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { //int shit = qtrue; } } */ /* ============= PM_CorrectAllSolid ============= */ static int PM_CorrectAllSolid( trace_t *trace ) { int i, j, k; vec3_t point; if ( pm->debugLevel ) { Com_Printf("%i:allsolid\n", c_pmove); } // jitter around for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { for (k = -1; k <= 1; k++) { VectorCopy(pm->ps->origin, point); point[0] += (float) i; point[1] += (float) j; point[2] += (float) k; pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); if ( !trace->allsolid ) { point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] - 0.25; pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); pml.groundTrace = *trace; return qtrue; } } } } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; return qfalse; } /* ============= PM_GroundTraceMissed The ground trace didn't hit a surface, so we are in freefall ============= */ static void PM_GroundTraceMissed( void ) { trace_t trace; vec3_t point; if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) { // we just transitioned into freefall if ( pm->debugLevel ) { Com_Printf("%i:lift\n", c_pmove); } // if they aren't in a jumping animation and the ground is a ways away, force into it // if we didn't do the trace, the player would be backflipping down staircases VectorCopy( pm->ps->origin, point ); point[2] -= 64; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } } } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; } /* ============= PM_GroundTrace ============= */ static void PM_GroundTrace( void ) { vec3_t point; trace_t trace; point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] - 0.25; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); pml.groundTrace = trace; // do something corrective if the trace starts in a solid... if ( trace.allsolid ) { if ( !PM_CorrectAllSolid(&trace) ) return; } // if the trace didn't hit anything, we are in free fall if ( trace.fraction == 1.0 ) { PM_GroundTraceMissed(); pml.groundPlane = qfalse; pml.walking = qfalse; return; } // check if getting thrown off the ground if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) { if ( pm->debugLevel ) { Com_Printf("%i:kickoff\n", c_pmove); } // go into jump animation if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; return; } // slopes that are too steep will not be considered onground if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) { if ( pm->debugLevel ) { Com_Printf("%i:steep\n", c_pmove); } // FIXME: if they can't slide down the slope, let them // walk (sharp crevices) pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qtrue; pml.walking = qfalse; return; } pml.groundPlane = qtrue; pml.walking = qtrue; // hitting solid ground will end a waterjump if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); pm->ps->pm_time = 0; } if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { // just hit the ground if ( pm->debugLevel ) { Com_Printf("%i:Land\n", c_pmove); } PM_CrashLand(); // don't do landing time if we were just going down a slope if ( pml.previous_velocity[2] < -200 ) { // don't allow another jump for a little while pm->ps->pm_flags |= PMF_TIME_LAND; pm->ps->pm_time = 250; } } pm->ps->groundEntityNum = trace.entityNum; // don't reset the z velocity for slopes // pm->ps->velocity[2] = 0; PM_AddTouchEnt( trace.entityNum ); } /* ============= PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving ============= */ static void PM_SetWaterLevel( void ) { vec3_t point; int cont; int sample1; int sample2; // // get waterlevel, accounting for ducking // pm->waterlevel = 0; pm->watertype = 0; point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] + MINS_Z + 1; cont = pm->pointcontents( point, pm->ps->clientNum ); if ( cont & MASK_WATER ) { sample2 = pm->ps->viewheight - MINS_Z; sample1 = sample2 / 2; pm->watertype = cont; pm->waterlevel = 1; point[2] = pm->ps->origin[2] + MINS_Z + sample1; cont = pm->pointcontents (point, pm->ps->clientNum ); if ( cont & MASK_WATER ) { pm->waterlevel = 2; point[2] = pm->ps->origin[2] + MINS_Z + sample2; cont = pm->pointcontents (point, pm->ps->clientNum ); if ( cont & MASK_WATER ){ pm->waterlevel = 3; } } } } /* ============== PM_CheckDuck Sets mins, maxs, and pm->ps->viewheight ============== */ static void PM_CheckDuck (void) { trace_t trace; if ( pm->ps->powerups[PW_INVULNERABILITY] ) { if ( pm->ps->pm_flags & PMF_INVULEXPAND ) { // invulnerability sphere has a 42 units radius VectorSet( pm->mins, -42, -42, -42 ); VectorSet( pm->maxs, 42, 42, 42 ); } else { VectorSet( pm->mins, -15, -15, MINS_Z ); VectorSet( pm->maxs, 15, 15, 16 ); } pm->ps->pm_flags |= PMF_DUCKED; pm->ps->viewheight = CROUCH_VIEWHEIGHT; return; } pm->ps->pm_flags &= ~PMF_INVULEXPAND; pm->mins[0] = -15; pm->mins[1] = -15; pm->maxs[0] = 15; pm->maxs[1] = 15; pm->mins[2] = MINS_Z; if (pm->ps->pm_type == PM_DEAD) { pm->maxs[2] = -8; pm->ps->viewheight = DEAD_VIEWHEIGHT; return; } if (pm->cmd.upmove < 0) { // duck pm->ps->pm_flags |= PMF_DUCKED; } else { // stand up if possible if (pm->ps->pm_flags & PMF_DUCKED) { // try to stand up pm->maxs[2] = 32; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask ); if (!trace.allsolid) pm->ps->pm_flags &= ~PMF_DUCKED; } } if (pm->ps->pm_flags & PMF_DUCKED) { pm->maxs[2] = 16; pm->ps->viewheight = CROUCH_VIEWHEIGHT; } else { pm->maxs[2] = 32; pm->ps->viewheight = DEFAULT_VIEWHEIGHT; } } //=================================================================== /* =============== PM_Footsteps =============== */ static void PM_Footsteps( void ) { float bobmove; int old; qboolean footstep; // // calculate speed and cycle to be used for // all cyclic walking effects // pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0] + pm->ps->velocity[1] * pm->ps->velocity[1] ); if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { if ( pm->ps->powerups[PW_INVULNERABILITY] ) { PM_ContinueLegsAnim( LEGS_IDLECR ); } // airborne leaves position in cycle intact, but doesn't advance if ( pm->waterlevel > 1 ) { PM_ContinueLegsAnim( LEGS_SWIM ); } return; } // if not trying to move if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) { if ( pm->xyspeed < 5 ) { pm->ps->bobCycle = 0; // start at beginning of cycle again if ( pm->ps->pm_flags & PMF_DUCKED ) { PM_ContinueLegsAnim( LEGS_IDLECR ); } else { PM_ContinueLegsAnim( LEGS_IDLE ); } } return; } footstep = qfalse; if ( pm->ps->pm_flags & PMF_DUCKED ) { bobmove = 0.5; // ducked characters bob much faster if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACKCR ); } else { PM_ContinueLegsAnim( LEGS_WALKCR ); } // ducked characters never play footsteps /* } else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { bobmove = 0.4; // faster speeds bob faster footstep = qtrue; } else { bobmove = 0.3; } PM_ContinueLegsAnim( LEGS_BACK ); */ } else { if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { bobmove = 0.4f; // faster speeds bob faster if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACK ); } else { PM_ContinueLegsAnim( LEGS_RUN ); } footstep = qtrue; } else { bobmove = 0.3f; // walking bobs slow if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACKWALK ); } else { PM_ContinueLegsAnim( LEGS_WALK ); } } } // check for footstep / splash sounds old = pm->ps->bobCycle; pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; // if we just crossed a cycle boundary, play an apropriate footstep event if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) { if ( pm->waterlevel == 0 ) { // on ground will only play sounds if running if ( footstep && !pm->noFootsteps ) { PM_AddEvent( PM_FootstepForSurface() ); } } else if ( pm->waterlevel == 1 ) { // splashing PM_AddEvent( EV_FOOTSPLASH ); } else if ( pm->waterlevel == 2 ) { // wading / swimming at surface PM_AddEvent( EV_SWIM ); } else if ( pm->waterlevel == 3 ) { // no sound when completely underwater } } } /* ============== PM_WaterEvents Generate sound events for entering and leaving water ============== */ static void PM_WaterEvents( void ) { // FIXME? // // if just entered a water volume, play a sound // if (!pml.previous_waterlevel && pm->waterlevel) { PM_AddEvent( EV_WATER_TOUCH ); } // // if just completely exited a water volume, play a sound // if (pml.previous_waterlevel && !pm->waterlevel) { PM_AddEvent( EV_WATER_LEAVE ); } // // check for head just going under water // if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) { PM_AddEvent( EV_WATER_UNDER ); } // // check for head just coming out of water // if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) { PM_AddEvent( EV_WATER_CLEAR ); } } /* =============== PM_BeginWeaponChange =============== */ static void PM_BeginWeaponChange( int weapon ) { if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) { return; } if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { return; } if ( pm->ps->weaponstate == WEAPON_DROPPING ) { return; } if(pm->pmove_flags & DF_INSTANT_WEAPON_CHANGE) { pm->ps->weaponstate = WEAPON_DROPPING; } else { PM_AddEvent( EV_CHANGE_WEAPON ); pm->ps->weaponstate = WEAPON_DROPPING; pm->ps->weaponTime += 200; PM_StartTorsoAnim( TORSO_DROP ); } } /* =============== PM_FinishWeaponChange =============== */ static void PM_FinishWeaponChange( void ) { int weapon; weapon = pm->cmd.weapon; if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { weapon = WP_NONE; } if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { weapon = WP_NONE; } pm->ps->weapon = weapon; pm->ps->weaponstate = WEAPON_RAISING; if(! (pm->pmove_flags & DF_INSTANT_WEAPON_CHANGE)) { pm->ps->weaponTime += 250; PM_StartTorsoAnim( TORSO_RAISE ); } } /* ============== PM_TorsoAnimation ============== */ static void PM_TorsoAnimation( void ) { if ( pm->ps->weaponstate == WEAPON_READY ) { if ( pm->ps->weapon == WP_GAUNTLET ) { PM_ContinueTorsoAnim( TORSO_STAND2 ); } else { PM_ContinueTorsoAnim( TORSO_STAND ); } return; } } /* ============== PM_Weapon Generates weapon events and modifes the weapon counter Elimination TODO: Make this thing stop during warmup (done) ============== */ static void PM_Weapon( void ) { int addTime; // don't allow attack until all buttons are up if ( pm->ps->pm_flags & PMF_RESPAWNED ) { return; } // ignore if spectator if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR || pm->ps->pm_type == PM_SPECTATOR) { return; } // check for dead player if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { pm->ps->weapon = WP_NONE; return; } // check for item using if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) { if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) { if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) { // don't use medkit if at max health } else { pm->ps->pm_flags |= PMF_USE_ITEM_HELD; PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag ); pm->ps->stats[STAT_HOLDABLE_ITEM] = 0; } return; } } else { pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD; } // make weapon function if ( pm->ps->weaponTime > 0 ) { pm->ps->weaponTime -= pml.msec; } // check for weapon change // can't change if weapon is firing, but can change // again if lowering or raising if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) { if ( pm->ps->weapon != pm->cmd.weapon ) { PM_BeginWeaponChange( pm->cmd.weapon ); } } if ( pm->ps->weaponTime > 0 ) { return; } // change weapon if time if ( pm->ps->weaponstate == WEAPON_DROPPING ) { PM_FinishWeaponChange(); return; } if ( pm->ps->weaponstate == WEAPON_RAISING ) { pm->ps->weaponstate = WEAPON_READY; if ( pm->ps->weapon == WP_GAUNTLET ) { PM_StartTorsoAnim( TORSO_STAND2 ); } else { PM_StartTorsoAnim( TORSO_STAND ); } return; } // check for fire if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) { pm->ps->weaponTime = 0; pm->ps->weaponstate = WEAPON_READY; return; } // start the animation even if out of ammo if ( pm->ps->weapon == WP_GAUNTLET ) { // the guantlet only "fires" when it actually hits something if ( !pm->gauntletHit ) { pm->ps->weaponTime = 0; pm->ps->weaponstate = WEAPON_READY; return; } PM_StartTorsoAnim( TORSO_ATTACK2 ); } else { PM_StartTorsoAnim( TORSO_ATTACK ); } pm->ps->weaponstate = WEAPON_FIRING; // check for out of ammo if ( ! pm->ps->ammo[ pm->ps->weapon ] ) { PM_AddEvent( EV_NOAMMO ); pm->ps->weaponTime += 500; return; } // take an ammo away if not infinite, 999 or up if ( !(pm->ps->ammo[ pm->ps->weapon ] == -1 || pm->ps->ammo[ pm->ps->weapon ] >=999 )) { pm->ps->ammo[ pm->ps->weapon ]--; } // fire weapon PM_AddEvent( EV_FIRE_WEAPON ); switch( pm->ps->weapon ) { default: case WP_GAUNTLET: addTime = 400; break; case WP_LIGHTNING: addTime = 50; break; case WP_SHOTGUN: addTime = 1000; break; case WP_MACHINEGUN: addTime = 100; break; case WP_GRENADE_LAUNCHER: addTime = 800; break; case WP_ROCKET_LAUNCHER: addTime = 800; break; case WP_PLASMAGUN: addTime = 100; break; case WP_RAILGUN: addTime = 1500; break; case WP_BFG: addTime = 200; break; case WP_GRAPPLING_HOOK: addTime = 400; break; case WP_NAILGUN: addTime = 1000; break; case WP_PROX_LAUNCHER: addTime = 800; break; case WP_CHAINGUN: addTime = 30; break; } if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { addTime /= 1.5; } else if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { addTime /= 1.3; } else if ( pm->ps->powerups[PW_HASTE] ) { addTime /= 1.3; } pm->ps->weaponTime += addTime; } /* ================ PM_Animate ================ */ static void PM_Animate( void ) { if ( pm->cmd.buttons & BUTTON_GESTURE ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_GESTURE ); pm->ps->torsoTimer = TIMER_GESTURE; PM_AddEvent( EV_TAUNT ); } } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_GETFLAG ); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_GUARDBASE ); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } else if ( pm->cmd.buttons & BUTTON_PATROL ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_PATROL ); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_FOLLOWME ); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_AFFIRMATIVE); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_NEGATIVE ); pm->ps->torsoTimer = 600; //TIMER_GESTURE; } } } /* ================ PM_DropTimers ================ */ static void PM_DropTimers( void ) { // drop misc timing counter if ( pm->ps->pm_time ) { if ( pml.msec >= pm->ps->pm_time ) { pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } else { pm->ps->pm_time -= pml.msec; } } // drop animation counter if ( pm->ps->legsTimer > 0 ) { pm->ps->legsTimer -= pml.msec; if ( pm->ps->legsTimer < 0 ) { pm->ps->legsTimer = 0; } } if ( pm->ps->torsoTimer > 0 ) { pm->ps->torsoTimer -= pml.msec; if ( pm->ps->torsoTimer < 0 ) { pm->ps->torsoTimer = 0; } } } /* ================ PM_UpdateViewAngles This can be used as another entry point when only the viewangles are being updated isntead of a full move ================ */ void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) { short temp; int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) { return; // no view changes at all } if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) { return; // no view changes at all } // circularly clamp the angles with deltas for (i=0 ; i<3 ; i++) { temp = cmd->angles[i] + ps->delta_angles[i]; if ( i == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) { ps->delta_angles[i] = 16000 - cmd->angles[i]; temp = 16000; } else if ( temp < -16000 ) { ps->delta_angles[i] = -16000 - cmd->angles[i]; temp = -16000; } } ps->viewangles[i] = SHORT2ANGLE(temp); } } /* ================ PmoveSingle ================ */ void trap_SnapVector( float *v ); void PmoveSingle (pmove_t *pmove) { pm = pmove; // this counter lets us debug movement problems with a journal // by setting a conditional breakpoint fot the previous frame c_pmove++; // clear results pm->numtouch = 0; pm->watertype = 0; pm->waterlevel = 0; if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies } // make sure walking button is clear if they are running, to avoid // proxy no-footsteps cheats if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) { pm->cmd.buttons &= ~BUTTON_WALKING; } // set the talk balloon flag if ( pm->cmd.buttons & BUTTON_TALK ) { pm->ps->eFlags |= EF_TALK; } else { pm->ps->eFlags &= ~EF_TALK; } // set the firing flag for continuous beam weapons if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION && pm->ps->pm_type != PM_NOCLIP && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) { pm->ps->eFlags |= EF_FIRING; } else { pm->ps->eFlags &= ~EF_FIRING; } // clear the respawned flag if attack and use are cleared if ( pm->ps->stats[STAT_HEALTH] > 0 && !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) { pm->ps->pm_flags &= ~PMF_RESPAWNED; } // if talk button is down, dissallow all other input // this is to prevent any possible intercept proxy from // adding fake talk balloons if ( pmove->cmd.buttons & BUTTON_TALK ) { // keep the talk button set tho for when the cmd.serverTime > 66 msec // and the same cmd is used multiple times in Pmove pmove->cmd.buttons = BUTTON_TALK; pmove->cmd.forwardmove = 0; pmove->cmd.rightmove = 0; pmove->cmd.upmove = 0; } // clear all pmove local vars memset (&pml, 0, sizeof(pml)); // determine the time pml.msec = pmove->cmd.serverTime - pm->ps->commandTime; if ( pml.msec < 1 ) { pml.msec = 1; } else if ( pml.msec > 200 ) { pml.msec = 200; } pm->ps->commandTime = pmove->cmd.serverTime; // save old org in case we get stuck VectorCopy (pm->ps->origin, pml.previous_origin); // save old velocity for crashlanding VectorCopy (pm->ps->velocity, pml.previous_velocity); pml.frametime = pml.msec * 0.001; // update the viewangles PM_UpdateViewAngles( pm->ps, &pm->cmd ); AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up); if ( pm->cmd.upmove < 10 ) { // not holding jump pm->ps->pm_flags &= ~PMF_JUMP_HELD; } // decide if backpedaling animations should be used if ( pm->cmd.forwardmove < 0 ) { pm->ps->pm_flags |= PMF_BACKWARDS_RUN; } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN; } if ( pm->ps->pm_type >= PM_DEAD ) { pm->cmd.forwardmove = 0; pm->cmd.rightmove = 0; pm->cmd.upmove = 0; } if ( pm->ps->pm_type == PM_SPECTATOR ) { PM_CheckDuck (); PM_FlyMove (); PM_DropTimers (); return; } if ( pm->ps->pm_type == PM_NOCLIP ) { PM_NoclipMove (); PM_DropTimers (); return; } if (pm->ps->pm_type == PM_FREEZE) { return; // no movement at all } if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) { return; // no movement at all } // set watertype, and waterlevel PM_SetWaterLevel(); pml.previous_waterlevel = pmove->waterlevel; // set mins, maxs, and viewheight PM_CheckDuck (); // set groundentity PM_GroundTrace(); if ( pm->ps->pm_type == PM_DEAD ) { PM_DeadMove (); } PM_DropTimers(); if ( pm->ps->powerups[PW_INVULNERABILITY] ) { PM_InvulnerabilityMove(); } else if ( pm->ps->powerups[PW_FLIGHT] ) { // flight powerup doesn't allow jump and has different friction PM_FlyMove(); } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) { PM_GrappleMove(); // We can wiggle a bit PM_AirMove(); } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { PM_WaterJumpMove(); } else if ( pm->waterlevel > 1 ) { // swimming PM_WaterMove(); } else if ( pml.walking ) { // walking on ground PM_WalkMove(); } else { // airborne PM_AirMove(); } PM_Animate(); // set groundentity, watertype, and waterlevel PM_GroundTrace(); PM_SetWaterLevel(); // weapons if(!(pm->ps->pm_flags & PMF_ELIMWARMUP)) PM_Weapon(); // torso animation PM_TorsoAnimation(); // footstep events / legs animations PM_Footsteps(); // entering / leaving water splashes PM_WaterEvents(); // snap some parts of playerstate to save network bandwidth //But only if pmove_float is not enabled if(!(pm->pmove_float)) trap_SnapVector( pm->ps->velocity ); } /* ================ Pmove Can be called by either the server or the client ================ */ void Pmove (pmove_t *pmove) { int finalTime; finalTime = pmove->cmd.serverTime; if ( finalTime < pmove->ps->commandTime ) { return; // should not happen } if ( finalTime > pmove->ps->commandTime + 1000 ) { pmove->ps->commandTime = finalTime - 1000; } pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<ps->commandTime != finalTime ) { int msec; msec = finalTime - pmove->ps->commandTime; if ( pmove->pmove_fixed ) { if ( msec > pmove->pmove_msec ) { msec = pmove->pmove_msec; } } else { if ( msec > 66 ) { msec = 66; } } pmove->cmd.serverTime = pmove->ps->commandTime + msec; PmoveSingle( pmove ); if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) { pmove->cmd.upmove = 20; } } //PM_CheckStuck(); } openarena_0.8.8.orig/code/game/g_utils.c0000644000175000017500000003620611656310264016672 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_utils.c -- misc utility functions for game module #include "g_local.h" typedef struct { char oldShader[MAX_QPATH]; char newShader[MAX_QPATH]; float timeOffset; } shaderRemap_t; #define MAX_SHADER_REMAPS 128 int remapCount = 0; shaderRemap_t remappedShaders[MAX_SHADER_REMAPS]; void AddRemap(const char *oldShader, const char *newShader, float timeOffset) { int i; for (i = 0; i < remapCount; i++) { if (Q_stricmp(oldShader, remappedShaders[i].oldShader) == 0) { // found it, just update this one strcpy(remappedShaders[i].newShader,newShader); remappedShaders[i].timeOffset = timeOffset; return; } } if (remapCount < MAX_SHADER_REMAPS) { strcpy(remappedShaders[remapCount].newShader,newShader); strcpy(remappedShaders[remapCount].oldShader,oldShader); remappedShaders[remapCount].timeOffset = timeOffset; remapCount++; } } const char *BuildShaderStateConfig(void) { static char buff[MAX_STRING_CHARS*4]; char out[(MAX_QPATH * 2) + 5]; int i; memset(buff, 0, MAX_STRING_CHARS); for (i = 0; i < remapCount; i++) { Com_sprintf(out, (MAX_QPATH * 2) + 5, "%s=%s:%5.2f@", remappedShaders[i].oldShader, remappedShaders[i].newShader, remappedShaders[i].timeOffset); Q_strcat( buff, sizeof( buff ), out); } return buff; } /* ========================================================================= model / sound configstring indexes ========================================================================= */ /* ================ G_FindConfigstringIndex ================ */ int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) { int i; char s[MAX_STRING_CHARS]; if ( !name || !name[0] ) { return 0; } for ( i=1 ; iinuse) continue; s = *(char **) ((byte *)from + fieldofs); if (!s) continue; if (!Q_stricmp (s, match)) return from; } return NULL; } /* ============= G_PickTarget Selects a random entity from among the targets ============= */ #define MAXCHOICES 32 gentity_t *G_PickTarget (char *targetname) { gentity_t *ent = NULL; int num_choices = 0; gentity_t *choice[MAXCHOICES]; if (!targetname) { G_Printf("G_PickTarget called with NULL targetname\n"); return NULL; } while(1) { ent = G_Find (ent, FOFS(targetname), targetname); if (!ent) break; choice[num_choices++] = ent; if (num_choices == MAXCHOICES) break; } if (!num_choices) { G_Printf("G_PickTarget: target %s not found\n", targetname); return NULL; } return choice[rand() % num_choices]; } /* ============================== G_UseTargets "activator" should be set to the entity that initiated the firing. Search for (string)targetname in all entities that match (string)self.target and call their .use function ============================== */ void G_UseTargets( gentity_t *ent, gentity_t *activator ) { gentity_t *t; if ( !ent ) { return; } if (ent->targetShaderName && ent->targetShaderNewName) { float f = level.time * 0.001; AddRemap(ent->targetShaderName, ent->targetShaderNewName, f); trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig()); } if ( !ent->target ) { return; } t = NULL; while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) { if ( t == ent ) { G_Printf ("WARNING: Entity used itself.\n"); } else { if ( t->use ) { t->use (t, ent, activator); } } if ( !ent->inuse ) { G_Printf("entity was removed while using targets\n"); return; } } } /* ============= TempVector This is just a convenience function for making temporary vectors for function calls ============= */ float *tv( float x, float y, float z ) { static int index; static vec3_t vecs[8]; float *v; // use an array so that multiple tempvectors won't collide // for a while v = vecs[index]; index = (index + 1)&7; v[0] = x; v[1] = y; v[2] = z; return v; } /* ============= VectorToString This is just a convenience function for printing vectors ============= */ char *vtos( const vec3_t v ) { static int index; static char str[8][32]; char *s; // use an array so that multiple vtos won't collide s = str[index]; index = (index + 1)&7; Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]); return s; } /* =============== G_SetMovedir The editor only specifies a single value for angles (yaw), but we have special constants to generate an up or down direction. Angles will be cleared, because it is being used to represent a direction instead of an orientation. =============== */ void G_SetMovedir( vec3_t angles, vec3_t movedir ) { static vec3_t VEC_UP = {0, -1, 0}; static vec3_t MOVEDIR_UP = {0, 0, 1}; static vec3_t VEC_DOWN = {0, -2, 0}; static vec3_t MOVEDIR_DOWN = {0, 0, -1}; if ( VectorCompare (angles, VEC_UP) ) { VectorCopy (MOVEDIR_UP, movedir); } else if ( VectorCompare (angles, VEC_DOWN) ) { VectorCopy (MOVEDIR_DOWN, movedir); } else { AngleVectors (angles, movedir, NULL, NULL); } VectorClear( angles ); } float vectoyaw( const vec3_t vec ) { float yaw; if (vec[YAW] == 0 && vec[PITCH] == 0) { yaw = 0; } else { if (vec[PITCH]) { yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI ); } else if (vec[YAW] > 0) { yaw = 90; } else { yaw = 270; } if (yaw < 0) { yaw += 360; } } return yaw; } void G_InitGentity( gentity_t *e ) { e->inuse = qtrue; e->classname = "noclass"; e->s.number = e - g_entities; e->r.ownerNum = ENTITYNUM_NONE; } /* ================= G_Spawn Either finds a free entity, or allocates a new one. The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will never be used by anything else. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ gentity_t *G_Spawn( void ) { int i, force; gentity_t *e; e = NULL; // shut up warning i = 0; // shut up warning for ( force = 0 ; force < 2 ; force++ ) { // if we go through all entities and can't find one to free, // override the normal minimum times before use e = &g_entities[MAX_CLIENTS]; for ( i = MAX_CLIENTS ; iinuse ) { continue; } // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 ) { continue; } // reuse this slot G_InitGentity( e ); return e; } if ( i != MAX_GENTITIES ) { break; } } if ( i == ENTITYNUM_MAX_NORMAL ) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } G_Error( "G_Spawn: no free entities" ); } // open up a new slot level.num_entities++; // let the server system know that there are more entities trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), &level.clients[0].ps, sizeof( level.clients[0] ) ); G_InitGentity( e ); return e; } /* ================= G_EntitiesFree ================= */ qboolean G_EntitiesFree( void ) { int i; gentity_t *e; e = &g_entities[MAX_CLIENTS]; for ( i = MAX_CLIENTS; i < level.num_entities; i++, e++) { if ( e->inuse ) { continue; } // slot available return qtrue; } return qfalse; } /* ================= G_FreeEntity Marks the entity as free ================= */ void G_FreeEntity( gentity_t *ed ) { trap_UnlinkEntity (ed); // unlink from world if ( ed->neverFree ) { return; } memset (ed, 0, sizeof(*ed)); ed->classname = "freed"; ed->freetime = level.time; ed->inuse = qfalse; } /* ================= G_TempEntity Spawns an event entity that will be auto-removed The origin will be snapped to save net bandwidth, so care must be taken if the origin is right on a surface (snap towards start vector first) ================= */ gentity_t *G_TempEntity( vec3_t origin, int event ) { gentity_t *e; vec3_t snapped; e = G_Spawn(); e->s.eType = ET_EVENTS + event; e->classname = "tempEntity"; e->eventTime = level.time; e->freeAfterEvent = qtrue; VectorCopy( origin, snapped ); SnapVector( snapped ); // save network bandwidth G_SetOrigin( e, snapped ); // find cluster for PVS trap_LinkEntity( e ); return e; } /* ============================================================================== Kill box ============================================================================== */ /* ================= G_KillBox Kills all entities that would touch the proposed new positioning of ent. Ent should be unlinked before calling this! ================= */ void G_KillBox (gentity_t *ent) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; vec3_t mins, maxs; VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); for (i=0 ; iclient ) { continue; } // nail it G_Damage ( hit, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } } //============================================================================== /* =============== G_AddPredictableEvent Use for non-pmove events that would also be predicted on the client side: jumppads and item pickups Adds an event+parm and twiddles the event counter =============== */ void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) { if ( !ent->client ) { return; } BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps ); } /* =============== G_AddEvent Adds an event+parm and twiddles the event counter =============== */ void G_AddEvent( gentity_t *ent, int event, int eventParm ) { int bits; if ( !event ) { G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number ); return; } // clients need to add the event in playerState_t instead of entityState_t if ( ent->client ) { bits = ent->client->ps.externalEvent & EV_EVENT_BITS; bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS; ent->client->ps.externalEvent = event | bits; ent->client->ps.externalEventParm = eventParm; ent->client->ps.externalEventTime = level.time; } else { bits = ent->s.event & EV_EVENT_BITS; bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS; ent->s.event = event | bits; ent->s.eventParm = eventParm; } ent->eventTime = level.time; } /* ============= G_Sound ============= */ void G_Sound( gentity_t *ent, int channel, int soundIndex ) { gentity_t *te; te = G_TempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND ); te->s.eventParm = soundIndex; } /* ============= G_GlobalSound KK-OAX G_SoundIndex must first be called. ============= */ void G_GlobalSound( int soundIndex ) { gentity_t *te; //Let's avoid the S_FindName error if soundIndex is 0. //Sago: And let's check that the sound index is within the allowed range. if( ( soundIndex <= 0 ) || soundIndex >= MAX_SOUNDS ) { //Display this message when debugging #ifdef DEBUG G_Printf( "GlobalSound: Error, no soundIndex specified. Check your code!\n" ); #endif return; } //Spawn a Temporary Entity at the origin point for Intermission with the event EV_GLOBAL_SOUND te = G_TempEntity( level.intermission_origin, EV_GLOBAL_SOUND ); //Add the soundIndex to the parameters for the EV_GLOBAL_SOUND event we are calling te->s.eventParm = soundIndex; //Broadcast the sound event. te->r.svFlags |= SVF_BROADCAST; } //============================================================================== /* ================ G_SetOrigin Sets the pos trajectory for a fixed position ================ */ void G_SetOrigin( gentity_t *ent, vec3_t origin ) { VectorCopy( origin, ent->s.pos.trBase ); ent->s.pos.trType = TR_STATIONARY; ent->s.pos.trTime = 0; ent->s.pos.trDuration = 0; VectorClear( ent->s.pos.trDelta ); VectorCopy( origin, ent->r.currentOrigin ); } /* ================ DebugLine debug polygons only work when running a local game with r_debugSurface set to 2 ================ */ int DebugLine(vec3_t start, vec3_t end, int color) { vec3_t points[4], dir, cross, up = {0, 0, 1}; float dot; VectorCopy(start, points[0]); VectorCopy(start, points[1]); //points[1][2] -= 2; VectorCopy(end, points[2]); //points[2][2] -= 2; VectorCopy(end, points[3]); VectorSubtract(end, start, dir); VectorNormalize(dir); dot = DotProduct(dir, up); if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); else CrossProduct(dir, up, cross); VectorNormalize(cross); VectorMA(points[0], 2, cross, points[0]); VectorMA(points[1], -2, cross, points[1]); VectorMA(points[2], -2, cross, points[2]); VectorMA(points[3], 2, cross, points[3]); return trap_DebugPolygonCreate(color, 4, points); } openarena_0.8.8.orig/code/game/g_main.c0000644000175000017500000026032611711076136016457 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" level_locals_t level; typedef struct { vmCvar_t *vmCvar; char *cvarName; char *defaultString; int cvarFlags; int modificationCount; // for tracking changes qboolean trackChange; // track this variable, and announce if changed qboolean teamShader; // track and if changed, update shader state } cvarTable_t; gentity_t g_entities[MAX_GENTITIES]; gclient_t g_clients[MAX_CLIENTS]; vmCvar_t g_gametype; vmCvar_t g_dmflags; vmCvar_t g_videoflags; vmCvar_t g_elimflags; vmCvar_t g_voteflags; vmCvar_t g_fraglimit; vmCvar_t g_timelimit; vmCvar_t g_capturelimit; vmCvar_t g_friendlyFire; vmCvar_t g_password; vmCvar_t g_needpass; vmCvar_t g_maxclients; vmCvar_t g_maxGameClients; vmCvar_t g_dedicated; vmCvar_t g_speed; vmCvar_t g_gravity; vmCvar_t g_gravityModifier; vmCvar_t g_damageModifier; vmCvar_t g_cheats; vmCvar_t g_knockback; vmCvar_t g_quadfactor; vmCvar_t g_forcerespawn; vmCvar_t g_respawntime; vmCvar_t g_inactivity; vmCvar_t g_debugMove; vmCvar_t g_debugDamage; vmCvar_t g_debugAlloc; vmCvar_t g_weaponRespawn; vmCvar_t g_weaponTeamRespawn; vmCvar_t g_motd; vmCvar_t g_motdfile; vmCvar_t g_votemaps; vmCvar_t g_votecustom; vmCvar_t g_synchronousClients; vmCvar_t g_warmup; vmCvar_t g_doWarmup; vmCvar_t g_restarted; vmCvar_t g_logfile; vmCvar_t g_logfileSync; vmCvar_t g_blood; vmCvar_t g_podiumDist; vmCvar_t g_podiumDrop; vmCvar_t g_allowVote; vmCvar_t g_teamAutoJoin; vmCvar_t g_teamForceBalance; vmCvar_t g_banIPs; vmCvar_t g_filterBan; vmCvar_t g_smoothClients; vmCvar_t pmove_fixed; vmCvar_t pmove_msec; vmCvar_t pmove_float; vmCvar_t g_rankings; vmCvar_t g_listEntity; vmCvar_t g_obeliskHealth; vmCvar_t g_obeliskRegenPeriod; vmCvar_t g_obeliskRegenAmount; vmCvar_t g_obeliskRespawnDelay; vmCvar_t g_cubeTimeout; #ifdef MISSIONPACK vmCvar_t g_redteam; vmCvar_t g_blueteam; vmCvar_t g_singlePlayer; #endif vmCvar_t g_enableDust; vmCvar_t g_enableBreath; vmCvar_t g_proxMineTimeout; vmCvar_t g_music; vmCvar_t g_spawnprotect; //Following for elimination: vmCvar_t g_elimination_selfdamage; vmCvar_t g_elimination_startHealth; vmCvar_t g_elimination_startArmor; vmCvar_t g_elimination_bfg; vmCvar_t g_elimination_grapple; vmCvar_t g_elimination_roundtime; vmCvar_t g_elimination_warmup; vmCvar_t g_elimination_activewarmup; vmCvar_t g_elimination_allgametypes; vmCvar_t g_elimination_machinegun; vmCvar_t g_elimination_shotgun; vmCvar_t g_elimination_grenade; vmCvar_t g_elimination_rocket; vmCvar_t g_elimination_railgun; vmCvar_t g_elimination_lightning; vmCvar_t g_elimination_plasmagun; vmCvar_t g_elimination_chain; vmCvar_t g_elimination_mine; vmCvar_t g_elimination_nail; vmCvar_t g_elimination_lockspectator; vmCvar_t g_rockets; //dmn_clowns suggestions (with my idea of implementing): vmCvar_t g_instantgib; vmCvar_t g_vampire; vmCvar_t g_vampireMaxHealth; //Regen vmCvar_t g_regen; int g_ffa_gt; //Are this a FFA gametype even if gametype is high? vmCvar_t g_lms_lives; vmCvar_t g_lms_mode; vmCvar_t g_elimination_ctf_oneway; vmCvar_t g_awardpushing; //The server can decide if players are awarded for pushing people in lave etc. vmCvar_t g_persistantpowerups; //Allow missionpack style persistant powerups? vmCvar_t g_catchup; //Favors the week players vmCvar_t g_autonextmap; //Autochange map vmCvar_t g_mappools; //mappools to be used for autochange vmCvar_t g_voteNames; vmCvar_t g_voteBan; vmCvar_t g_voteGametypes; vmCvar_t g_voteMinTimelimit; vmCvar_t g_voteMaxTimelimit; vmCvar_t g_voteMinFraglimit; vmCvar_t g_voteMaxFraglimit; vmCvar_t g_maxvotes; vmCvar_t g_humanplayers; //used for voIP vmCvar_t g_redTeamClientNumbers; vmCvar_t g_blueTeamClientNumbers; //unlagged - server options vmCvar_t g_delagHitscan; vmCvar_t g_truePing; vmCvar_t sv_fps; vmCvar_t g_lagLightning; //Adds a little lag to the lightninggun to make it less powerfull //unlagged - server options //KK-OAX vmCvar_t g_sprees; vmCvar_t g_altExcellent; vmCvar_t g_spreeDiv; //Command/Chat spamming/flooding vmCvar_t g_floodMaxDemerits; vmCvar_t g_floodMinTime; //Admin vmCvar_t g_admin; vmCvar_t g_adminLog; vmCvar_t g_adminParseSay; vmCvar_t g_adminNameProtect; vmCvar_t g_adminTempBan; vmCvar_t g_adminMaxBan; vmCvar_t g_specChat; vmCvar_t g_publicAdminMessages; vmCvar_t g_maxWarnings; vmCvar_t g_warningExpire; vmCvar_t g_minNameChangePeriod; vmCvar_t g_maxNameChanges; vmCvar_t g_timestamp_startgame; // bk001129 - made static to avoid aliasing static cvarTable_t gameCvarTable[] = { // don't override the cheat state set by the system { &g_cheats, "sv_cheats", "", 0, 0, qfalse }, // noset vars { NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse }, { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse }, { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, // latched vars { &g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse }, { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse }, { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse }, // change anytime vars { &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, { &g_videoflags, "videoflags", "7", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, { &g_elimflags, "elimflags", "0", CVAR_SERVERINFO, 0, qfalse }, { &g_voteflags, "voteflags", "0", CVAR_SERVERINFO, 0, qfalse }, { &g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse }, { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue }, { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE }, { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE }, { &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue }, { &g_doWarmup, "g_doWarmup", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, { &g_logfile, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse }, { &g_logfileSync, "g_logsync", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse }, { &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse }, { &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, { &g_dedicated, "dedicated", "0", 0, 0, qfalse }, { &g_speed, "g_speed", "320", 0, 0, qtrue }, { &g_gravity, "g_gravity", "800", 0, 0, qtrue }, { &g_gravityModifier, "g_gravityModifier", "1", 0, 0, qtrue }, { &g_damageModifier, "g_damageModifier", "0", 0, 0, qtrue }, { &g_knockback, "g_knockback", "1000", 0, 0, qtrue }, { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue }, { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue }, { &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue }, { &g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue }, { &g_respawntime, "g_respawntime", "0", CVAR_ARCHIVE, 0, qtrue }, { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue }, { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse }, { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse }, { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse }, { &g_motd, "g_motd", "", 0, 0, qfalse }, { &g_motdfile, "g_motdfile", "motd.cfg", 0, 0, qfalse }, { &g_blood, "com_blood", "1", 0, 0, qfalse }, { &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse }, { &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse }, //Votes start: { &g_allowVote, "g_allowVote", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_maxvotes, "g_maxVotes", MAX_VOTE_COUNT, CVAR_ARCHIVE, 0, qfalse }, { &g_voteNames, "g_voteNames", "/map_restart/nextmap/map/g_gametype/kick/clientkick/g_doWarmup/timelimit/fraglimit/shuffle/", CVAR_ARCHIVE, 0, qfalse }, //clientkick g_doWarmup timelimit fraglimit { &g_voteBan, "g_voteBan", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_voteGametypes, "g_voteGametypes", "/0/1/3/4/5/6/7/8/9/10/11/12/", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_voteMaxTimelimit, "g_voteMaxTimelimit", "1000", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_voteMinTimelimit, "g_voteMinTimelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_voteMaxFraglimit, "g_voteMaxFraglimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_voteMinFraglimit, "g_voteMinFraglimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_votemaps, "g_votemapsfile", "votemaps.cfg", 0, 0, qfalse }, { &g_votecustom, "g_votecustomfile", "votecustom.cfg", 0, 0, qfalse }, { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse }, { &g_obeliskHealth, "g_obeliskHealth", "2500", 0, 0, qfalse }, { &g_obeliskRegenPeriod, "g_obeliskRegenPeriod", "1", 0, 0, qfalse }, { &g_obeliskRegenAmount, "g_obeliskRegenAmount", "15", 0, 0, qfalse }, { &g_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO, 0, qfalse }, { &g_cubeTimeout, "g_cubeTimeout", "30", 0, 0, qfalse }, #ifdef MISSIONPACK { &g_redteam, "g_redteam", "Stroggs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue }, { &g_blueteam, "g_blueteam", "Pagans", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue }, { &g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse }, #endif { &g_enableDust, "g_enableDust", "0", CVAR_SERVERINFO, 0, qtrue, qfalse }, { &g_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO, 0, qtrue, qfalse }, { &g_proxMineTimeout, "g_proxMineTimeout", "20000", 0, 0, qfalse }, { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse}, { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO | CVAR_ARCHIVE, 0, qfalse}, { &pmove_msec, "pmove_msec", "11", CVAR_SYSTEMINFO | CVAR_ARCHIVE, 0, qfalse}, { &pmove_float, "pmove_float", "1", CVAR_SYSTEMINFO | CVAR_ARCHIVE, 0, qtrue}, //unlagged - server options { &g_delagHitscan, "g_delagHitscan", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qtrue }, { &g_truePing, "g_truePing", "0", CVAR_ARCHIVE, 0, qtrue }, // it's CVAR_SYSTEMINFO so the client's sv_fps will be automagically set to its value { &sv_fps, "sv_fps", "20", CVAR_SYSTEMINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_lagLightning, "g_lagLightning", "1", CVAR_ARCHIVE, 0, qtrue }, //unlagged - server options { &g_rankings, "g_rankings", "0", 0, 0, qfalse}, { &g_music, "g_music", "", 0, 0, qfalse}, { &g_spawnprotect, "g_spawnprotect", "500", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue}, //Now for elimination stuff: { &g_elimination_selfdamage, "elimination_selfdamage", "0", 0, 0, qtrue }, { &g_elimination_startHealth, "elimination_startHealth", "200", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_elimination_startArmor, "elimination_startArmor", "150", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_elimination_bfg, "elimination_bfg", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_grapple, "elimination_grapple", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_roundtime, "elimination_roundtime", "120", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_elimination_warmup, "elimination_warmup", "7", CVAR_ARCHIVE | CVAR_NORESTART , 0, qtrue }, { &g_elimination_activewarmup, "elimination_activewarmup", "5", CVAR_ARCHIVE | CVAR_NORESTART , 0, qtrue }, { &g_elimination_allgametypes, "g_elimination", "0", CVAR_LATCH | CVAR_NORESTART, 0, qfalse }, { &g_elimination_machinegun, "elimination_machinegun", "500", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_shotgun, "elimination_shotgun", "500", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_grenade, "elimination_grenade", "100", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_rocket, "elimination_rocket", "50", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_railgun, "elimination_railgun", "20", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_lightning, "elimination_lightning", "300", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_plasmagun, "elimination_plasmagun", "200", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_chain, "elimination_chain", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_mine, "elimination_mine", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_nail, "elimination_nail", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_ctf_oneway, "elimination_ctf_oneway", "0", CVAR_ARCHIVE| CVAR_NORESTART, 0, qtrue }, { &g_elimination_lockspectator, "elimination_lockspectator", "0", CVAR_NORESTART, 0, qtrue }, { &g_awardpushing, "g_awardpushing", "1", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, //g_persistantpowerups #ifdef MISSIONPACK { &g_persistantpowerups, "g_runes", "1", CVAR_LATCH, 0, qfalse }, #else { &g_persistantpowerups, "g_runes", "0", CVAR_LATCH|CVAR_ARCHIVE, 0, qfalse }, #endif //nexuiz style rocket arena { &g_rockets, "g_rockets", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_NORESTART, 0, qfalse }, //Instantgib and Vampire thingies { &g_instantgib, "g_instantgib", "0", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse }, { &g_vampire, "g_vampire", "0.0", CVAR_NORESTART, 0, qtrue }, { &g_regen, "g_regen", "0", CVAR_NORESTART, 0, qtrue }, { &g_vampireMaxHealth, "g_vampire_max_health", "500", CVAR_NORESTART, 0, qtrue }, { &g_lms_lives, "g_lms_lives", "1", CVAR_NORESTART, 0, qtrue }, { &g_lms_mode, "g_lms_mode", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_catchup, "g_catchup", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue}, { &g_autonextmap, "g_autonextmap", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qfalse}, { &g_mappools, "g_mappools", "0\\maps_dm.cfg\\1\\maps_tourney.cfg\\3\\maps_tdm.cfg\\4\\maps_ctf.cfg\\5\\maps_oneflag.cfg\\6\\maps_obelisk.cfg\ \\7\\maps_harvester.cfg\\8\\maps_elimination.cfg\\9\\maps_ctf.cfg\\10\\maps_lms.cfg\\11\\maps_dd.cfg\\12\\maps_dom.cfg\\", CVAR_ARCHIVE | CVAR_NORESTART, 0, qfalse}, { &g_humanplayers, "g_humanplayers", "0", CVAR_ROM | CVAR_NORESTART, 0, qfalse }, //used for voIP { &g_redTeamClientNumbers, "g_redTeamClientNumbers", "0",CVAR_ROM, 0, qfalse }, { &g_blueTeamClientNumbers, "g_blueTeamClientNumbers", "0",CVAR_ROM, 0, qfalse }, //KK-OAX { &g_sprees, "g_sprees", "sprees.dat", 0, 0, qfalse }, { &g_altExcellent, "g_altExcellent", "0", CVAR_SERVERINFO, 0, qtrue}, { &g_spreeDiv, "g_spreeDiv", "5", 0, 0, qfalse}, //Used for command/chat flooding { &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse }, { &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse }, //Admin { &g_admin, "g_admin", "admin.dat", CVAR_ARCHIVE, 0, qfalse }, { &g_adminLog, "g_adminLog", "admin.log", CVAR_ARCHIVE, 0, qfalse }, { &g_adminParseSay, "g_adminParseSay", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_adminTempBan, "g_adminTempBan", "2m", CVAR_ARCHIVE, 0, qfalse }, { &g_adminMaxBan, "g_adminMaxBan", "2w", CVAR_ARCHIVE, 0, qfalse }, { &g_specChat, "g_specChat", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_publicAdminMessages, "g_publicAdminMessages", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_maxWarnings, "g_maxWarnings", "3", CVAR_ARCHIVE, 0, qfalse }, { &g_warningExpire, "g_warningExpire", "3600", CVAR_ARCHIVE, 0, qfalse }, { &g_minNameChangePeriod, "g_minNameChangePeriod", "10", 0, 0, qfalse}, { &g_maxNameChanges, "g_maxNameChanges", "50", 0, 0, qfalse}, { &g_timestamp_startgame, "g_timestamp", "0001-01-01 00:00:00", CVAR_SERVERINFO, 0, qfalse} }; // bk001129 - made static to avoid aliasing static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[0] ); void G_InitGame( int levelTime, int randomSeed, int restart ); void G_RunFrame( int levelTime ); void G_ShutdownGame( int restart ); void CheckExitRules( void ); /* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case GAME_INIT: G_InitGame( arg0, arg1, arg2 ); return 0; case GAME_SHUTDOWN: G_ShutdownGame( arg0 ); return 0; case GAME_CLIENT_CONNECT: return (intptr_t)ClientConnect( arg0, arg1, arg2 ); case GAME_CLIENT_THINK: ClientThink( arg0 ); return 0; case GAME_CLIENT_USERINFO_CHANGED: ClientUserinfoChanged( arg0 ); return 0; case GAME_CLIENT_DISCONNECT: ClientDisconnect( arg0 ); return 0; case GAME_CLIENT_BEGIN: ClientBegin( arg0 ); return 0; case GAME_CLIENT_COMMAND: ClientCommand( arg0 ); return 0; case GAME_RUN_FRAME: G_RunFrame( arg0 ); return 0; case GAME_CONSOLE_COMMAND: return ConsoleCommand(); case BOTAI_START_FRAME: return BotAIStartFrame( arg0 ); } return -1; } void QDECL G_Printf( const char *fmt, ... ) { va_list argptr; char text[1024]; va_start (argptr, fmt); Q_vsnprintf (text, sizeof(text), fmt, argptr); va_end (argptr); trap_Printf( text ); } void QDECL G_Error( const char *fmt, ... ) { va_list argptr; char text[1024]; va_start (argptr, fmt); Q_vsnprintf (text, sizeof(text), fmt, argptr); va_end (argptr); trap_Error( text ); } /* ================ G_FindTeams Chain together all entities with a matching team field. Entity teams are used for item groups and multi-entity mover groups. All but the first will have the FL_TEAMSLAVE flag set and teammaster field set All but the last will have the teamchain field set to the next one ================ */ void G_FindTeams( void ) { gentity_t *e, *e2; int i, j; int c, c2; c = 0; c2 = 0; for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){ if (!e->inuse) continue; if (!e->team) continue; if (e->flags & FL_TEAMSLAVE) continue; e->teammaster = e; c++; c2++; for (j=i+1, e2=e+1 ; j < level.num_entities ; j++,e2++) { if (!e2->inuse) continue; if (!e2->team) continue; if (e2->flags & FL_TEAMSLAVE) continue; if (!strcmp(e->team, e2->team)) { c2++; e2->teamchain = e->teamchain; e->teamchain = e2; e2->teammaster = e; e2->flags |= FL_TEAMSLAVE; // make sure that targets only point at the master if ( e2->targetname ) { e->targetname = e2->targetname; e2->targetname = NULL; } } } } G_Printf ("%i teams with %i entities\n", c, c2); } void G_RemapTeamShaders( void ) { #ifdef MISSIONPACK char string[1024]; float f = level.time * 0.001; Com_sprintf( string, sizeof(string), "team_icon/%s_red", g_redteam.string ); AddRemap("textures/ctf2/redteam01", string, f); AddRemap("textures/ctf2/redteam02", string, f); Com_sprintf( string, sizeof(string), "team_icon/%s_blue", g_blueteam.string ); AddRemap("textures/ctf2/blueteam01", string, f); AddRemap("textures/ctf2/blueteam02", string, f); trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig()); #endif } /* ================= G_RegisterCvars ================= */ void G_RegisterCvars( void ) { int i; cvarTable_t *cv; qboolean remapped = qfalse; for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); if ( cv->vmCvar ) cv->modificationCount = cv->vmCvar->modificationCount; if (cv->teamShader) { remapped = qtrue; } } if (remapped) { G_RemapTeamShaders(); } // check some things if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) { G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer ); trap_Cvar_Set( "g_gametype", "0" ); } //set FFA status for high gametypes: if ( g_gametype.integer == GT_LMS ) { g_ffa_gt = 1; //Last Man standig is a FFA gametype } else { g_ffa_gt = 0; //If >GT_CTF use bases } level.warmupModificationCount = g_warmup.modificationCount; } /* ================= G_UpdateCvars ================= */ void G_UpdateCvars( void ) { int i; cvarTable_t *cv; qboolean remapped = qfalse; for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) { if ( cv->vmCvar ) { trap_Cvar_Update( cv->vmCvar ); if ( cv->modificationCount != cv->vmCvar->modificationCount ) { cv->modificationCount = cv->vmCvar->modificationCount; if ( cv->trackChange ) { trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"", cv->cvarName, cv->vmCvar->string ) ); } if ( cv->vmCvar == &g_votecustom ) VoteParseCustomVotes(); //Here comes the cvars that must trigger a map_restart if (cv->vmCvar == &g_instantgib || cv->vmCvar == &g_rockets || cv->vmCvar == &g_elimination_allgametypes) { trap_Cvar_Set("sv_dorestart","1"); } if ( cv->vmCvar == &g_voteNames ) { //Set vote flags int voteflags=0; if( allowedVote("map_restart") ) voteflags|=VF_map_restart; if( allowedVote("map") ) voteflags|=VF_map; if( allowedVote("clientkick") ) voteflags|=VF_clientkick; if( allowedVote("shuffle") ) voteflags|=VF_shuffle; if( allowedVote("nextmap") ) voteflags|=VF_nextmap; if( allowedVote("g_gametype") ) voteflags|=VF_g_gametype; if( allowedVote("g_doWarmup") ) voteflags|=VF_g_doWarmup; if( allowedVote("timelimit") ) voteflags|=VF_timelimit; if( allowedVote("fraglimit") ) voteflags|=VF_fraglimit; if( allowedVote("custom") ) voteflags|=VF_custom; trap_Cvar_Set("voteflags",va("%i",voteflags)); } if (cv->teamShader) { remapped = qtrue; } } } } if (remapped) { G_RemapTeamShaders(); } } /* Sets the cvar g_timestamp. Return 0 if success or !0 for errors. */ int G_UpdateTimestamp( void ) { int ret = 0; qtime_t timestamp; ret = trap_RealTime(×tamp); trap_Cvar_Set("g_timestamp",va("%04i-%02i-%02i %02i:%02i:%02i", 1900+timestamp.tm_year,1+timestamp.tm_mon, timestamp.tm_mday, timestamp.tm_hour,timestamp.tm_min,timestamp.tm_sec)); return ret; } /* ============ G_InitGame ============ */ void G_InitGame( int levelTime, int randomSeed, int restart ) { int i; G_Printf ("------- Game Initialization -------\n"); G_Printf ("gamename: %s\n", GAMEVERSION); G_Printf ("gamedate: %s\n", __DATE__); srand( randomSeed ); G_RegisterCvars(); G_UpdateTimestamp(); //disable unwanted cvars if( g_gametype.integer == GT_SINGLE_PLAYER ) { g_instantgib.integer = 0; g_rockets.integer = 0; g_vampire.value = 0.0f; } G_ProcessIPBans(); //KK-OAX Changed to Tremulous's BG_InitMemory BG_InitMemory(); // set some level globals memset( &level, 0, sizeof( level ) ); level.time = levelTime; level.startTime = levelTime; level.snd_fry = G_SoundIndex("sound/player/fry.wav"); // FIXME standing in lava / slime if ( g_gametype.integer != GT_SINGLE_PLAYER && g_logfile.string[0] ) { if ( g_logfileSync.integer ) { trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND_SYNC ); } else { trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND ); } if ( !level.logFile ) { G_Printf( "WARNING: Couldn't open logfile: %s\n", g_logfile.string ); } else { char serverinfo[MAX_INFO_STRING]; trap_GetServerinfo( serverinfo, sizeof( serverinfo ) ); G_LogPrintf("------------------------------------------------------------\n" ); G_LogPrintf("InitGame: %s\n", serverinfo ); G_LogPrintf("Info: ServerInfo length: %d of %d\n", strlen(serverinfo), MAX_INFO_STRING ); } } else { G_Printf( "Not logging to disk.\n" ); } //Parse the custom vote names: VoteParseCustomVotes(); G_InitWorldSession(); //KK-OAX Get Admin Configuration G_admin_readconfig( NULL, 0 ); //Let's Load up any killing sprees/multikills G_ReadAltKillSettings( NULL, 0 ); // initialize all entities for this game memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) ); level.gentities = g_entities; // initialize all clients for this game level.maxclients = g_maxclients.integer; memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) ); level.clients = g_clients; // set client fields on player ents for ( i=0 ; i= GT_TEAM && (g_ffa_gt!=1)) { G_CheckTeamItems(); } SaveRegisteredItems(); G_Printf ("-----------------------------------\n"); if( g_gametype.integer == GT_SINGLE_PLAYER || trap_Cvar_VariableIntegerValue( "com_buildScript" ) ) { G_ModelIndex( SP_PODIUM_MODEL ); } if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { BotAISetup( restart ); BotAILoadMap( restart ); G_InitBots( restart ); } G_RemapTeamShaders(); //elimination: level.roundNumber = 1; level.roundNumberStarted = 0; level.roundStartTime = level.time+g_elimination_warmup.integer*1000; level.roundRespawned = qfalse; level.eliminationSides = rand()%2; //0 or 1 //Challenges: level.teamSize = 0; level.hadBots = qfalse; if(g_gametype.integer == GT_DOUBLE_D) Team_SpawnDoubleDominationPoints(); if(g_gametype.integer == GT_DOMINATION ){ level.dom_scoreGiven = 0; for(i=0;i= 2 ) { return; } // never change during intermission if ( level.intermissiontime ) { return; } nextInLine = NULL; for ( i = 0 ; i < level.maxclients ; i++ ) { client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } // never select the dedicated follow or scoreboard clients if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD || client->sess.spectatorClient < 0 ) { continue; } if(!nextInLine || client->sess.spectatorNum > nextInLine->sess.spectatorNum) { nextInLine = client; } } if ( !nextInLine ) { return; } level.warmupTime = -1; // set them to free-for-all team SetTeam( &g_entities[ nextInLine - level.clients ], "f" ); } /* ======================= AddTournamentQueue Add client to end of tournament queue ======================= */ void AddTournamentQueue(gclient_t *client) { int index; gclient_t *curclient; for(index = 0; index < level.maxclients; index++) { curclient = &level.clients[index]; if(curclient->pers.connected != CON_DISCONNECTED) { if(curclient == client) curclient->sess.spectatorNum = 0; else if(curclient->sess.sessionTeam == TEAM_SPECTATOR) curclient->sess.spectatorNum++; } } } /* ======================= RemoveTournamentLoser Make the loser a spectator at the back of the line ======================= */ void RemoveTournamentLoser( void ) { int clientNum; if ( level.numPlayingClients != 2 ) { return; } clientNum = level.sortedClients[1]; if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) { return; } // make them a spectator SetTeam( &g_entities[ clientNum ], "s" ); } /* ======================= RemoveTournamentWinner ======================= */ void RemoveTournamentWinner( void ) { int clientNum; if ( level.numPlayingClients != 2 ) { return; } clientNum = level.sortedClients[0]; if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) { return; } // make them a spectator SetTeam( &g_entities[ clientNum ], "s" ); } /* ======================= AdjustTournamentScores ======================= */ void AdjustTournamentScores( void ) { int clientNum; clientNum = level.sortedClients[0]; if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) { level.clients[ clientNum ].sess.wins++; ClientUserinfoChanged( clientNum ); } clientNum = level.sortedClients[1]; if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) { level.clients[ clientNum ].sess.losses++; ClientUserinfoChanged( clientNum ); } } /* ============= SortRanks ============= */ int QDECL SortRanks( const void *a, const void *b ) { gclient_t *ca, *cb; ca = &level.clients[*(int *)a]; cb = &level.clients[*(int *)b]; // sort special clients last if ( ca->sess.spectatorState == SPECTATOR_SCOREBOARD || ca->sess.spectatorClient < 0 ) { return 1; } if ( cb->sess.spectatorState == SPECTATOR_SCOREBOARD || cb->sess.spectatorClient < 0 ) { return -1; } // then connecting clients if ( ca->pers.connected == CON_CONNECTING ) { return 1; } if ( cb->pers.connected == CON_CONNECTING ) { return -1; } // then spectators if ( ca->sess.sessionTeam == TEAM_SPECTATOR && cb->sess.sessionTeam == TEAM_SPECTATOR ) { if ( ca->sess.spectatorNum > cb->sess.spectatorNum ) { return -1; } if ( ca->sess.spectatorNum < cb->sess.spectatorNum ) { return 1; } return 0; } if ( ca->sess.sessionTeam == TEAM_SPECTATOR ) { return 1; } if ( cb->sess.sessionTeam == TEAM_SPECTATOR ) { return -1; } //In elimination and CTF elimination, sort dead players last if((g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && level.roundNumber==level.roundNumberStarted && (ca->isEliminated != cb->isEliminated)) { if( ca->isEliminated ) return 1; if( cb->isEliminated ) return -1; } // then sort by score if ( ca->ps.persistant[PERS_SCORE] > cb->ps.persistant[PERS_SCORE] ) { return -1; } if ( ca->ps.persistant[PERS_SCORE] < cb->ps.persistant[PERS_SCORE] ) { return 1; } return 0; } /* ============ CalculateRanks Recalculates the score ranks of all players This will be called on every client connect, begin, disconnect, death, and team change. ============ */ void CalculateRanks( void ) { int i; int rank; int score; int newScore; int humanplayers; gclient_t *cl; level.follow1 = -1; level.follow2 = -1; level.numConnectedClients = 0; level.numNonSpectatorClients = 0; level.numPlayingClients = 0; humanplayers = 0; // don't count bots for ( i = 0; i < TEAM_NUM_TEAMS; i++ ) { level.numteamVotingClients[i] = 0; } for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected != CON_DISCONNECTED ) { level.sortedClients[level.numConnectedClients] = i; level.numConnectedClients++; //We just set humanplayers to 0 during intermission if ( !level.intermissiontime && level.clients[i].pers.connected == CON_CONNECTED && !(g_entities[i].r.svFlags & SVF_BOT) ) { humanplayers++; } if ( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR ) { level.numNonSpectatorClients++; // decide if this should be auto-followed if ( level.clients[i].pers.connected == CON_CONNECTED ) { level.numPlayingClients++; if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { if ( level.clients[i].sess.sessionTeam == TEAM_RED ) level.numteamVotingClients[0]++; else if ( level.clients[i].sess.sessionTeam == TEAM_BLUE ) level.numteamVotingClients[1]++; } if ( level.follow1 == -1 ) { level.follow1 = i; } else if ( level.follow2 == -1 ) { level.follow2 = i; } } } } } qsort( level.sortedClients, level.numConnectedClients, sizeof(level.sortedClients[0]), SortRanks ); // set the rank value for all clients that are connected and not spectators if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied for ( i = 0; i < level.numConnectedClients; i++ ) { cl = &level.clients[ level.sortedClients[i] ]; if ( level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] ) { cl->ps.persistant[PERS_RANK] = 2; } else if ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) { cl->ps.persistant[PERS_RANK] = 0; } else { cl->ps.persistant[PERS_RANK] = 1; } } } else { rank = -1; score = 0; for ( i = 0; i < level.numPlayingClients; i++ ) { cl = &level.clients[ level.sortedClients[i] ]; newScore = cl->ps.persistant[PERS_SCORE]; if ( i == 0 || newScore != score ) { rank = i; // assume we aren't tied until the next client is checked level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank; } else { // we are tied with the previous client level.clients[ level.sortedClients[i-1] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } score = newScore; if ( g_gametype.integer == GT_SINGLE_PLAYER && level.numPlayingClients == 1 ) { level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } } } // set the CS_SCORES1/2 configstrings, which will be visible to everyone if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { trap_SetConfigstring( CS_SCORES1, va("%i", level.teamScores[TEAM_RED] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", level.teamScores[TEAM_BLUE] ) ); } else { if ( level.numConnectedClients == 0 ) { trap_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) ); trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else if ( level.numConnectedClients == 1 ) { trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else { trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] ) ); } } // see if it is time to end the level CheckExitRules(); // if we are at the intermission, send the new info to everyone if ( level.intermissiontime ) { SendScoreboardMessageToAllClients(); } if(g_humanplayers.integer != humanplayers) //Presume all spectators are humans! trap_Cvar_Set( "g_humanplayers", va("%i", humanplayers) ); } /* ======================================================================== MAP CHANGING ======================================================================== */ /* ======================== SendScoreboardMessageToAllClients Do this at BeginIntermission time and whenever ranks are recalculated due to enters/exits/forced team changes ======================== */ void SendScoreboardMessageToAllClients( void ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED ) { DeathmatchScoreboardMessage( g_entities + i ); EliminationMessage( g_entities + i ); } } } /* ======================== SendElimiantionMessageToAllClients Used to send information important to Elimination ======================== */ void SendEliminationMessageToAllClients( void ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED ) { EliminationMessage( g_entities + i ); } } } /* ======================== SendDDtimetakenMessageToAllClients Do this if a team just started dominating. ======================== */ void SendDDtimetakenMessageToAllClients( void ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED ) { DoubleDominationScoreTimeMessage( g_entities + i ); } } } /* ======================== SendAttackingTeamMessageToAllClients Used for CTF Elimination oneway ======================== */ void SendAttackingTeamMessageToAllClients( void ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED ) { AttackingTeamMessage( g_entities + i ); } } } /* ======================== SendDominationPointsStatusMessageToAllClients Used for Standard domination ======================== */ void SendDominationPointsStatusMessageToAllClients( void ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED ) { DominationPointStatusMessage( g_entities + i ); } } } /* ======================== SendYourTeamMessageToTeam Tell all players on a given team who there allies are. Used for VoIP ======================== */ void SendYourTeamMessageToTeam( team_t team ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected == CON_CONNECTED && level.clients[ i ].sess.sessionTeam == team ) { YourTeamMessage( g_entities + i ); } } } /* ======================== MoveClientToIntermission When the intermission starts, this will be called for all players. If a new client connects, this will be called after the spawn function. ======================== */ void MoveClientToIntermission( gentity_t *ent ) { // take out of follow mode if needed if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } FindIntermissionPoint(); // move to the spot VectorCopy( level.intermission_origin, ent->s.origin ); VectorCopy( level.intermission_origin, ent->client->ps.origin ); VectorCopy (level.intermission_angle, ent->client->ps.viewangles); ent->client->ps.pm_type = PM_INTERMISSION; // clean up powerup info memset( ent->client->ps.powerups, 0, sizeof(ent->client->ps.powerups) ); ent->client->ps.eFlags = 0; ent->s.eFlags = 0; ent->s.eType = ET_GENERAL; ent->s.modelindex = 0; ent->s.loopSound = 0; ent->s.event = 0; ent->r.contents = 0; } /* ================== FindIntermissionPoint This is also used for spectator spawns ================== */ void FindIntermissionPoint( void ) { gentity_t *ent, *target; vec3_t dir; // find the intermission spot ent = G_Find (NULL, FOFS(classname), "info_player_intermission"); if ( !ent ) { // the map creator forgot to put in an intermission point... SelectSpawnPoint ( vec3_origin, level.intermission_origin, level.intermission_angle ); } else { VectorCopy (ent->s.origin, level.intermission_origin); VectorCopy (ent->s.angles, level.intermission_angle); // if it has a target, look towards it if ( ent->target ) { target = G_PickTarget( ent->target ); if ( target ) { VectorSubtract( target->s.origin, level.intermission_origin, dir ); vectoangles( dir, level.intermission_angle ); } } } } /* ================== BeginIntermission ================== */ void BeginIntermission( void ) { int i; gentity_t *client; if ( level.intermissiontime ) { return; // already active } // if in tournement mode, change the wins / losses if ( g_gametype.integer == GT_TOURNAMENT ) { AdjustTournamentScores(); } level.intermissiontime = level.time; // move all clients to the intermission point for (i=0 ; i< level.maxclients ; i++) { client = g_entities + i; if (!client->inuse) continue; // respawn if dead if (client->health <= 0) { ClientRespawn(client); } MoveClientToIntermission( client ); } #ifdef MISSIONPACK if (g_singlePlayer.integer) { trap_Cvar_Set("ui_singlePlayerActive", "0"); UpdateTournamentInfo(); } #else // if single player game if ( g_gametype.integer == GT_SINGLE_PLAYER ) { UpdateTournamentInfo(); SpawnModelsOnVictoryPads(); } #endif // send the current scoring to all clients SendScoreboardMessageToAllClients(); } /* ============= ExitLevel When the intermission has been exited, the server is either killed or moved to a new level based on the "nextmap" cvar ============= */ void ExitLevel (void) { int i; gclient_t *cl; char nextmap[MAX_STRING_CHARS]; char d1[MAX_STRING_CHARS]; char serverinfo[MAX_INFO_STRING]; //bot interbreeding BotInterbreedEndMatch(); // if we are running a tournement map, kick the loser to spectator status, // which will automatically grab the next spectator and restart if ( g_gametype.integer == GT_TOURNAMENT ) { if ( !level.restarted ) { RemoveTournamentLoser(); trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; level.changemap = NULL; level.intermissiontime = 0; } return; } trap_Cvar_VariableStringBuffer( "nextmap", nextmap, sizeof(nextmap) ); trap_Cvar_VariableStringBuffer( "d1", d1, sizeof(d1) ); trap_GetServerinfo( serverinfo, sizeof( serverinfo ) ); //Here the game finds the nextmap if g_autonextmap is set if(g_autonextmap.integer ) { char filename[MAX_FILEPATH]; fileHandle_t file,mapfile; //Look in g_mappools.string for the file to look for maps in Q_strncpyz(filename,Info_ValueForKey(g_mappools.string, va("%i",g_gametype.integer)),MAX_FILEPATH); //If we found a filename: if(filename[0]) { //Read the file: /*int len =*/ trap_FS_FOpenFile(filename, &file, FS_READ); if(!file) trap_FS_FOpenFile(va("%s.org",filename), &file, FS_READ); if(file) { char buffer[4*1024]; // buffer to read file into char mapnames[1024][20]; // Array of mapnames in the map pool char *pointer; int choice, count=0; //The random choice from mapnames and count of mapnames int i; memset(&buffer,0,sizeof(buffer)); trap_FS_Read(&buffer,sizeof(buffer),file); pointer = buffer; while ( qtrue ) { Q_strncpyz(mapnames[count],COM_Parse( &pointer ),20); if ( !mapnames[count][0] ) { break; } G_Printf("Mapname in mappool: %s\n",mapnames[count]); count++; } trap_FS_FCloseFile(file); //It is possible that the maps in the file read are flawed, so we try up to ten times: for(i=0;i<10;i++) { choice = (count > 0)? rand()%count : 0; if(Q_strequal(mapnames[choice],Info_ValueForKey(serverinfo,"mapname"))) continue; //Now check that the map exists: trap_FS_FOpenFile(va("maps/%s.bsp",mapnames[choice]),&mapfile,FS_READ); if(mapfile) { G_Printf("Picked map number %i - %s\n",choice,mapnames[choice]); Q_strncpyz(nextmap,va("map %s",mapnames[choice]),sizeof(nextmap)); trap_Cvar_Set("nextmap",nextmap); trap_FS_FCloseFile(mapfile); break; } } } } } if( !Q_stricmp( nextmap, "map_restart 0" ) && Q_stricmp( d1, "" ) ) { trap_Cvar_Set( "nextmap", "vstr d2" ); trap_SendConsoleCommand( EXEC_APPEND, "vstr d1\n" ); } else { trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" ); } level.changemap = NULL; level.intermissiontime = 0; // reset all the scores so we don't enter the intermission again level.teamScores[TEAM_RED] = 0; level.teamScores[TEAM_BLUE] = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } cl->ps.persistant[PERS_SCORE] = 0; } // we need to do this here before chaning to CON_CONNECTING G_WriteSessionData(); // change all client states to connecting, so the early players into the // next level will know the others aren't done reconnecting for (i=0 ; i< g_maxclients.integer ; i++) { if ( level.clients[i].pers.connected == CON_CONNECTED ) { level.clients[i].pers.connected = CON_CONNECTING; } } } /* ================= G_LogPrintf Print to the logfile with a time stamp if it is open ================= */ void QDECL G_LogPrintf( const char *fmt, ... ) { va_list argptr; char string[1024]; int min, tens, sec; sec = level.time / 1000; min = sec / 60; sec -= min * 60; tens = sec / 10; sec -= tens * 10; Com_sprintf( string, sizeof(string), "%3i:%i%i ", min, tens, sec ); va_start( argptr, fmt ); Q_vsnprintf(string + 7, sizeof(string) - 7, fmt, argptr); va_end( argptr ); if ( g_dedicated.integer ) { G_Printf( "%s", string + 7 ); } if ( !level.logFile ) { return; } trap_FS_Write( string, strlen( string ), level.logFile ); } /* ================ LogExit Append information about this game to the log file ================ */ void LogExit( const char *string ) { int i, numSorted; gclient_t *cl; #ifdef MISSIONPACK qboolean won = qtrue; #endif G_LogPrintf( "Exit: %s\n", string ); level.intermissionQueued = level.time; // this will keep the clients from playing any voice sounds // that will get cut off when the queued intermission starts trap_SetConfigstring( CS_INTERMISSION, "1" ); // don't send more than 32 scores (FIXME?) numSorted = level.numConnectedClients; if ( numSorted > 32 ) { numSorted = 32; } if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { G_LogPrintf( "red:%i blue:%i\n", level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE] ); } for (i=0 ; i < numSorted ; i++) { int ping; cl = &level.clients[level.sortedClients[i]]; if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } if ( cl->pers.connected == CON_CONNECTING ) { continue; } ping = cl->ps.ping < 999 ? cl->ps.ping : 999; G_LogPrintf( "score: %i ping: %i client: %i %s\n", cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i], cl->pers.netname ); #ifdef MISSIONPACK if (g_singlePlayer.integer && g_gametype.integer == GT_TOURNAMENT) { if (g_entities[cl - level.clients].r.svFlags & SVF_BOT && cl->ps.persistant[PERS_RANK] == 0) { won = qfalse; } } #endif } #ifdef MISSIONPACK if (g_singlePlayer.integer) { if (g_gametype.integer >= GT_CTF && g_ffa_gt==0) { won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]; } trap_SendConsoleCommand( EXEC_APPEND, (won) ? "spWin\n" : "spLose\n" ); } #endif } /* ================= CheckIntermissionExit The level will stay at the intermission for a minimum of 5 seconds If all players wish to continue, the level will then exit. If one or more players have not acknowledged the continue, the game will wait 10 seconds before going on. ================= */ void CheckIntermissionExit( void ) { int ready, notReady, playerCount; int i; gclient_t *cl; int readyMask; if ( g_gametype.integer == GT_SINGLE_PLAYER ) { return; } // see which players are ready ready = 0; notReady = 0; readyMask = 0; playerCount = 0; for (i=0 ; i< g_maxclients.integer ; i++) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) { continue; } playerCount++; if ( cl->readyToExit ) { ready++; if ( i < 16 ) { readyMask |= 1 << i; } } else { notReady++; } } // copy the readyMask to each player's stats so // it can be displayed on the scoreboard for (i=0 ; i< g_maxclients.integer ; i++) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } cl->ps.stats[STAT_CLIENTS_READY] = readyMask; } // never exit in less than five seconds if ( level.time < level.intermissiontime + 5000 ) { return; } // only test ready status when there are real players present if ( playerCount > 0 ) { // if nobody wants to go, clear timer if ( !ready ) { level.readyToExit = qfalse; return; } // if everyone wants to go, go now if ( !notReady ) { ExitLevel(); return; } } // the first person to ready starts the ten second timeout if ( !level.readyToExit ) { level.readyToExit = qtrue; level.exitTime = level.time; } // if we have waited ten seconds since at least one player // wanted to exit, go ahead if ( level.time < level.exitTime + 10000 ) { return; } ExitLevel(); } /* ============= ScoreIsTied ============= */ qboolean ScoreIsTied( void ) { int a, b; if ( level.numPlayingClients < 2 ) { return qfalse; } //Sago: In Elimination and Oneway Flag Capture teams must win by two points. if ( g_gametype.integer == GT_ELIMINATION || (g_gametype.integer == GT_CTF_ELIMINATION && g_elimination_ctf_oneway.integer)) { return (level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] || level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE]+1 || level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE]-1); } if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { return level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE]; } a = level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE]; b = level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE]; return a == b; } /* ================= CheckExitRules There will be a delay between the time the exit is qualified for and the time everyone is moved to the intermission spot, so you can see the last frag. ================= */ void CheckExitRules( void ) { int i; gclient_t *cl; // if at the intermission, wait for all non-bots to // signal ready, then go to next level if ( level.intermissiontime ) { CheckIntermissionExit (); return; } else { //sago: Find the reason for this to be neccesary. for (i=0 ; i< g_maxclients.integer ; i++) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } cl->ps.stats[STAT_CLIENTS_READY] = 0; } } if ( level.intermissionQueued ) { #ifdef MISSIONPACK int time = (g_singlePlayer.integer) ? SP_INTERMISSION_DELAY_TIME : INTERMISSION_DELAY_TIME; if ( level.time - level.intermissionQueued >= time ) { level.intermissionQueued = 0; BeginIntermission(); } #else if ( level.time - level.intermissionQueued >= INTERMISSION_DELAY_TIME ) { level.intermissionQueued = 0; BeginIntermission(); } #endif return; } // check for sudden death if ( ScoreIsTied() ) { // always wait for sudden death return; } if ( g_timelimit.integer > 0 && !level.warmupTime ) { if ( (level.time - level.startTime)/60000 >= g_timelimit.integer ) { trap_SendServerCommand( -1, "print \"Timelimit hit.\n\""); LogExit( "Timelimit hit." ); return; } } if ( level.numPlayingClients < 2 ) { return; } if ( (g_gametype.integer < GT_CTF || g_ffa_gt>0 ) && g_fraglimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) { trap_SendServerCommand( -1, "print \"Red hit the fraglimit.\n\"" ); LogExit( "Fraglimit hit." ); return; } if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer ) { trap_SendServerCommand( -1, "print \"Blue hit the fraglimit.\n\"" ); LogExit( "Fraglimit hit." ); return; } for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( cl->sess.sessionTeam != TEAM_FREE ) { continue; } if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) { LogExit( "Fraglimit hit." ); trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " hit the fraglimit.\n\"", cl->pers.netname ) ); return; } } } if ( (g_gametype.integer >= GT_CTF && g_ffa_gt<1) && g_capturelimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) { trap_SendServerCommand( -1, "print \"Red hit the capturelimit.\n\"" ); LogExit( "Capturelimit hit." ); return; } if ( level.teamScores[TEAM_BLUE] >= g_capturelimit.integer ) { trap_SendServerCommand( -1, "print \"Blue hit the capturelimit.\n\"" ); LogExit( "Capturelimit hit." ); return; } } } //LMS - Last man Stading functions: void StartLMSRound(void) { int countsLiving; countsLiving = TeamLivingCount( -1, TEAM_FREE ); if(countsLiving<2) { trap_SendServerCommand( -1, "print \"Not enough players to start the round\n\""); level.roundNumberStarted = level.roundNumber-1; level.roundStartTime = level.time+1000*g_elimination_warmup.integer; return; } //If we are enough to start a round: level.roundNumberStarted = level.roundNumber; //Set numbers G_LogPrintf( "LMS: %i %i %i: Round %i has started!\n", level.roundNumber, -1, 0, level.roundNumber ); SendEliminationMessageToAllClients(); EnableWeapons(); } //the elimination start function void StartEliminationRound(void) { int countsLiving[TEAM_NUM_TEAMS]; countsLiving[TEAM_BLUE] = TeamLivingCount( -1, TEAM_BLUE ); countsLiving[TEAM_RED] = TeamLivingCount( -1, TEAM_RED ); if((countsLiving[TEAM_BLUE]==0) || (countsLiving[TEAM_RED]==0)) { trap_SendServerCommand( -1, "print \"Not enough players to start the round\n\""); level.roundNumberStarted = level.roundNumber-1; level.roundRespawned = qfalse; //Remember that one of the teams is empty! level.roundRedPlayers = countsLiving[TEAM_RED]; level.roundBluePlayers = countsLiving[TEAM_BLUE]; level.roundStartTime = level.time+1000*g_elimination_warmup.integer; return; } //If we are enough to start a round: level.roundNumberStarted = level.roundNumber; //Set numbers level.roundRedPlayers = countsLiving[TEAM_RED]; level.roundBluePlayers = countsLiving[TEAM_BLUE]; if(g_gametype.integer == GT_CTF_ELIMINATION) { Team_ReturnFlag( TEAM_RED ); Team_ReturnFlag( TEAM_BLUE ); } if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: Round %i has started!\n", level.roundNumber, -1, 0, level.roundNumber ); } else if(g_gametype.integer == GT_CTF_ELIMINATION) { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: Round %i has started!\n", level.roundNumber, -1, -1, 4, level.roundNumber ); } SendEliminationMessageToAllClients(); if(g_elimination_ctf_oneway.integer) SendAttackingTeamMessageToAllClients(); //Ensure that evaryone know who should attack. EnableWeapons(); } //things to do at end of round: void EndEliminationRound(void) { DisableWeapons(); level.roundNumber++; level.roundStartTime = level.time+1000*g_elimination_warmup.integer; SendEliminationMessageToAllClients(); CalculateRanks(); level.roundRespawned = qfalse; if(g_elimination_ctf_oneway.integer) SendAttackingTeamMessageToAllClients(); } //Things to do if we don't want to move the roundNumber void RestartEliminationRound(void) { DisableWeapons(); level.roundNumberStarted = level.roundNumber-1; level.roundStartTime = level.time+1000*g_elimination_warmup.integer; if(!level.intermissiontime) SendEliminationMessageToAllClients(); level.roundRespawned = qfalse; if(g_elimination_ctf_oneway.integer) SendAttackingTeamMessageToAllClients(); } //Things to do during match warmup void WarmupEliminationRound(void) { EnableWeapons(); level.roundNumberStarted = level.roundNumber-1; level.roundStartTime = level.time+1000*g_elimination_warmup.integer; SendEliminationMessageToAllClients(); level.roundRespawned = qfalse; if(g_elimination_ctf_oneway.integer) SendAttackingTeamMessageToAllClients(); } /* ======================================================================== FUNCTIONS CALLED EVERY FRAME ======================================================================== */ /* CheckDoubleDomination */ void CheckDoubleDomination( void ) { if ( level.numPlayingClients < 1 ) { return; } if ( level.warmupTime != 0) { if( ((level.pointStatusA == TEAM_BLUE && level.pointStatusB == TEAM_BLUE) || (level.pointStatusA == TEAM_RED && level.pointStatusB == TEAM_RED)) && level.timeTaken + 10*1000 <= level.time ) { Team_RemoveDoubleDominationPoints(); level.roundStartTime = level.time + 10*1000; SendScoreboardMessageToAllClients(); } return; } if(g_gametype.integer != GT_DOUBLE_D) return; //Don't score if we are in intermission. Both points might have been taken when we went into intermission if(level.intermissiontime) return; if(level.pointStatusA == TEAM_RED && level.pointStatusB == TEAM_RED && level.timeTaken + 10*1000 <= level.time) { //Red scores trap_SendServerCommand( -1, "print \"Red team scores!\n\""); AddTeamScore(level.intermission_origin,TEAM_RED,1); G_LogPrintf( "DD: %i %i %i: %s scores!\n", -1, TEAM_RED, 2, TeamName(TEAM_RED) ); Team_ForceGesture(TEAM_RED); Team_DD_bonusAtPoints(TEAM_RED); Team_RemoveDoubleDominationPoints(); //We start again in 10 seconds: level.roundStartTime = level.time + 10*1000; SendScoreboardMessageToAllClients(); CalculateRanks(); } if(level.pointStatusA == TEAM_BLUE && level.pointStatusB == TEAM_BLUE && level.timeTaken + 10*1000 <= level.time) { //Blue scores trap_SendServerCommand( -1, "print \"Blue team scores!\n\""); AddTeamScore(level.intermission_origin,TEAM_BLUE,1); G_LogPrintf( "DD: %i %i %i: %s scores!\n", -1, TEAM_BLUE, 2, TeamName(TEAM_BLUE) ); Team_ForceGesture(TEAM_BLUE); Team_DD_bonusAtPoints(TEAM_BLUE); Team_RemoveDoubleDominationPoints(); //We start again in 10 seconds: level.roundStartTime = level.time + 10*1000; SendScoreboardMessageToAllClients(); CalculateRanks(); } if((level.pointStatusA == TEAM_NONE || level.pointStatusB == TEAM_NONE) && level.time>level.roundStartTime) { trap_SendServerCommand( -1, "print \"A new round has started\n\""); Team_SpawnDoubleDominationPoints(); SendScoreboardMessageToAllClients(); } } /* CheckLMS */ void CheckLMS(void) { int mode; mode = g_lms_mode.integer; if ( level.numPlayingClients < 1 ) { return; } //We don't want to do anything in intermission if(level.intermissiontime) { if(level.roundRespawned) { RestartEliminationRound(); } level.roundStartTime = level.time; //so that a player might join at any time to fix the bots+no humans+autojoin bug return; } if(g_gametype.integer == GT_LMS) { int countsLiving[TEAM_NUM_TEAMS]; //trap_SendServerCommand( -1, "print \"This is LMS!\n\""); countsLiving[TEAM_FREE] = TeamLivingCount( -1, TEAM_FREE ); if(countsLiving[TEAM_FREE]==1 && level.roundNumber==level.roundNumberStarted) { if(mode <=1 ) LMSpoint(); trap_SendServerCommand( -1, "print \"We have a winner!\n\""); EndEliminationRound(); Team_ForceGesture(TEAM_FREE); } if(countsLiving[TEAM_FREE]==0 && level.roundNumber==level.roundNumberStarted) { trap_SendServerCommand( -1, "print \"All death... how sad\n\""); EndEliminationRound(); } if((g_elimination_roundtime.integer) && (level.roundNumber==level.roundNumberStarted)&&(g_lms_mode.integer == 1 || g_lms_mode.integer==3)&&(level.time>=level.roundStartTime+1000*g_elimination_roundtime.integer)) { trap_SendServerCommand( -1, "print \"Time up - Overtime disabled\n\""); if(mode <=1 ) LMSpoint(); EndEliminationRound(); } //This might be better placed another place: if(g_elimination_activewarmup.integer<2) g_elimination_activewarmup.integer=2; //We need at least 2 seconds to spawn all players if(g_elimination_activewarmup.integer >= g_elimination_warmup.integer) //This must not be true g_elimination_warmup.integer = g_elimination_activewarmup.integer+1; //Increase warmup //Force respawn if(level.roundNumber != level.roundNumberStarted && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer && !level.roundRespawned) { level.roundRespawned = qtrue; RespawnAll(); DisableWeapons(); SendEliminationMessageToAllClients(); } if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer) { RespawnDead(); //DisableWeapons(); } if(level.roundNumber == level.roundNumberStarted) { EnableWeapons(); } if((level.roundNumber>level.roundNumberStarted)&&(level.time>=level.roundStartTime)) StartLMSRound(); if(level.time+1000*g_elimination_warmup.integer-500>level.roundStartTime && level.numPlayingClients < 2) { RespawnDead(); //Allow player to run around anyway WarmupEliminationRound(); //Start over return; } if(level.warmupTime != 0) { if(level.time+1000*g_elimination_warmup.integer-500>level.roundStartTime) { RespawnDead(); WarmupEliminationRound(); } } } } /* ============= CheckElimination ============= */ void CheckElimination(void) { if ( level.numPlayingClients < 1 ) { if( (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && ( level.time+1000*g_elimination_warmup.integer-500>level.roundStartTime )) RestartEliminationRound(); //For spectators return; } //We don't want to do anything in itnermission if(level.intermissiontime) { if(level.roundRespawned) RestartEliminationRound(); level.roundStartTime = level.time+1000*g_elimination_warmup.integer; return; } if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) { int counts[TEAM_NUM_TEAMS]; int countsLiving[TEAM_NUM_TEAMS]; int countsHealth[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( -1, TEAM_RED ); countsLiving[TEAM_BLUE] = TeamLivingCount( -1, TEAM_BLUE ); countsLiving[TEAM_RED] = TeamLivingCount( -1, TEAM_RED ); countsHealth[TEAM_BLUE] = TeamHealthCount( -1, TEAM_BLUE ); countsHealth[TEAM_RED] = TeamHealthCount( -1, TEAM_RED ); if(level.roundBluePlayers != 0 && level.roundRedPlayers != 0) { //Cannot score if one of the team never got any living players if((countsLiving[TEAM_BLUE]==0)&&(level.roundNumber==level.roundNumberStarted)) { //Blue team has been eliminated! trap_SendServerCommand( -1, "print \"Blue Team eliminated!\n\""); AddTeamScore(level.intermission_origin,TEAM_RED,1); if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: %s wins round %i by eleminating the enemy team!\n", level.roundNumber, TEAM_RED, 1, TeamName(TEAM_RED), level.roundNumber ); } else { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i by eleminating the enemy team!\n", level.roundNumber, -1, TEAM_RED, 6, TeamName(TEAM_RED), level.roundNumber ); } EndEliminationRound(); Team_ForceGesture(TEAM_RED); } else if((countsLiving[TEAM_RED]==0)&&(level.roundNumber==level.roundNumberStarted)) { //Red team eliminated! trap_SendServerCommand( -1, "print \"Red Team eliminated!\n\""); AddTeamScore(level.intermission_origin,TEAM_BLUE,1); if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: %s wins round %i by eleminating the enemy team!\n", level.roundNumber, TEAM_BLUE, 1, TeamName(TEAM_BLUE), level.roundNumber ); } else { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i by eleminating the enemy team!\n", level.roundNumber, -1, TEAM_BLUE, 6, TeamName(TEAM_BLUE), level.roundNumber ); } EndEliminationRound(); Team_ForceGesture(TEAM_BLUE); } } //Time up if((level.roundNumber==level.roundNumberStarted)&&(g_elimination_roundtime.integer)&&(level.time>=level.roundStartTime+1000*g_elimination_roundtime.integer)) { trap_SendServerCommand( -1, "print \"No teams eliminated.\n\""); if(level.roundBluePlayers != 0 && level.roundRedPlayers != 0) {//We don't want to divide by zero. (should not be possible) if(g_gametype.integer == GT_CTF_ELIMINATION && g_elimination_ctf_oneway.integer) { //One way CTF, make defensice team the winner. if ( (level.eliminationSides+level.roundNumber)%2 == 0 ) { //Red was attacking trap_SendServerCommand( -1, "print \"Blue team defended the base\n\""); AddTeamScore(level.intermission_origin,TEAM_BLUE,1); G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i by defending the flag!\n", level.roundNumber, -1, TEAM_BLUE, 5, TeamName(TEAM_BLUE), level.roundNumber ); } else { trap_SendServerCommand( -1, "print \"Red team defended the base\n\""); AddTeamScore(level.intermission_origin,TEAM_RED,1); G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i by defending the flag!\n", level.roundNumber, -1, TEAM_RED, 5, TeamName(TEAM_RED), level.roundNumber ); } } else if(((double)countsLiving[TEAM_RED])/((double)level.roundRedPlayers)>((double)countsLiving[TEAM_BLUE])/((double)level.roundBluePlayers)) { //Red team has higher procentage survivors trap_SendServerCommand( -1, "print \"Red team has most survivers!\n\""); AddTeamScore(level.intermission_origin,TEAM_RED,1); if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: %s wins round %i due to more survivors!\n", level.roundNumber, TEAM_RED, 2, TeamName(TEAM_RED), level.roundNumber ); } else { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i due to more survivors!\n", level.roundNumber, -1, TEAM_RED, 7, TeamName(TEAM_RED), level.roundNumber ); } } else if(((double)countsLiving[TEAM_RED])/((double)level.roundRedPlayers)<((double)countsLiving[TEAM_BLUE])/((double)level.roundBluePlayers)) { //Blue team has higher procentage survivors trap_SendServerCommand( -1, "print \"Blue team has most survivers!\n\""); AddTeamScore(level.intermission_origin,TEAM_BLUE,1); if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: %s wins round %i due to more survivors!\n", level.roundNumber, TEAM_BLUE, 2, TeamName(TEAM_BLUE), level.roundNumber ); } else { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i due to more survivors!\n", level.roundNumber, -1, TEAM_BLUE, 7, TeamName(TEAM_BLUE), level.roundNumber ); } } else if(countsHealth[TEAM_RED]>countsHealth[TEAM_BLUE]) { //Red team has more health trap_SendServerCommand( -1, "print \"Red team has more health left!\n\""); AddTeamScore(level.intermission_origin,TEAM_RED,1); if(g_gametype.integer == GT_ELIMINATION) { G_LogPrintf( "ELIMINATION: %i %i %i: %s wins round %i due to more health left!\n", level.roundNumber, TEAM_RED, 3, TeamName(TEAM_RED), level.roundNumber ); } else { G_LogPrintf( "CTF_ELIMINATION: %i %i %i %i: %s wins round %i due to more health left!\n", level.roundNumber, -1, TEAM_RED, 8, TeamName(TEAM_RED), level.roundNumber ); } } else if(countsHealth[TEAM_RED]= g_elimination_warmup.integer) //This must not be true g_elimination_warmup.integer = g_elimination_activewarmup.integer+1; //Increase warmup //Force respawn if(level.roundNumber!=level.roundNumberStarted && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer && !level.roundRespawned) { level.roundRespawned = qtrue; RespawnAll(); SendEliminationMessageToAllClients(); } if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer) { RespawnDead(); } if((level.roundNumber>level.roundNumberStarted)&&(level.time>=level.roundStartTime)) StartEliminationRound(); if(level.time+1000*g_elimination_warmup.integer-500>level.roundStartTime) if(counts[TEAM_BLUE]<1 || counts[TEAM_RED]<1) { RespawnDead(); //Allow players to run around anyway WarmupEliminationRound(); //Start over return; } if(level.warmupTime != 0) { if(level.time+1000*g_elimination_warmup.integer-500>level.roundStartTime) { RespawnDead(); WarmupEliminationRound(); } } } } /* ============= CheckDomination ============= */ void CheckDomination(void) { int i; int scoreFactor = 1; if ( (level.numPlayingClients < 1) || (g_gametype.integer != GT_DOMINATION) ) { return; } //Do nothing if warmup if(level.warmupTime != 0) return; //Don't score if we are in intermission. Just plain stupid if(level.intermissiontime) return; //Sago: I use if instead of while, since if the server stops for more than 2 seconds people should not be allowed to score anyway if(level.domination_points_count>3) scoreFactor = 2; //score more slowly if there are many points if(level.time>=level.dom_scoreGiven*DOM_SECSPERPOINT*scoreFactor) { for(i=0;ilevel.dom_scoreGiven*DOM_SECSPERPOINT*scoreFactor) level.dom_scoreGiven++; CalculateRanks(); } } /* ============= CheckTournament Once a frame, check for changes in tournement player state ============= */ void CheckTournament( void ) { // check because we run 3 game frames before calling Connect and/or ClientBegin // for clients on a map_restart if ( level.numPlayingClients == 0 ) { return; } if ( g_gametype.integer == GT_TOURNAMENT ) { // pull in a spectator if needed if ( level.numPlayingClients < 2 ) { AddTournamentPlayer(); } // if we don't have two players, go back to "waiting for players" if ( level.numPlayingClients != 2 ) { if ( level.warmupTime != -1 ) { level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } return; } if ( level.warmupTime == 0 ) { return; } // if the warmup is changed at the console, restart it if ( g_warmup.modificationCount != level.warmupModificationCount ) { level.warmupModificationCount = g_warmup.modificationCount; level.warmupTime = -1; } // if all players have arrived, start the countdown if ( level.warmupTime < 0 ) { if ( level.numPlayingClients == 2 ) { // fudge by -1 to account for extra delays if ( g_warmup.integer > 1 ) { level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000; } else { level.warmupTime = 0; } trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); } return; } // if the warmup time has counted down, restart if ( level.time > level.warmupTime ) { level.warmupTime += 10000; trap_Cvar_Set( "g_restarted", "1" ); trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; return; } } else if ( g_gametype.integer != GT_SINGLE_PLAYER && level.warmupTime != 0 ) { int counts[TEAM_NUM_TEAMS]; qboolean notEnough = qfalse; if ( g_gametype.integer > GT_TEAM && !g_ffa_gt ) { counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( -1, TEAM_RED ); if (counts[TEAM_RED] < 1 || counts[TEAM_BLUE] < 1) { notEnough = qtrue; } } else if ( level.numPlayingClients < 2 ) { notEnough = qtrue; } if ( notEnough ) { if ( level.warmupTime != -1 ) { level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } return; // still waiting for team members } if ( level.warmupTime == 0 ) { return; } // if the warmup is changed at the console, restart it if ( g_warmup.modificationCount != level.warmupModificationCount ) { level.warmupModificationCount = g_warmup.modificationCount; level.warmupTime = -1; } // if all players have arrived, start the countdown if ( level.warmupTime < 0 ) { // fudge by -1 to account for extra delays level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); return; } // if the warmup time has counted down, restart if ( level.time > level.warmupTime ) { level.warmupTime += 10000; trap_Cvar_Set( "g_restarted", "1" ); trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; return; } } } /* ================== PrintTeam ================== */ void PrintTeam(int team, char *message) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam != team) continue; trap_SendServerCommand( i, message ); } } /* ================== SetLeader ================== */ void SetLeader(int team, int client) { int i; if ( level.clients[client].pers.connected == CON_DISCONNECTED ) { PrintTeam(team, va("print \"%s is not connected\n\"", level.clients[client].pers.netname) ); return; } if (level.clients[client].sess.sessionTeam != team) { PrintTeam(team, va("print \"%s is not on the team anymore\n\"", level.clients[client].pers.netname) ); return; } for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam != team) continue; if (level.clients[i].sess.teamLeader) { level.clients[i].sess.teamLeader = qfalse; ClientUserinfoChanged(i); } } level.clients[client].sess.teamLeader = qtrue; ClientUserinfoChanged( client ); PrintTeam(team, va("print \"%s is the new team leader\n\"", level.clients[client].pers.netname) ); } /* ================== CheckTeamLeader ================== */ void CheckTeamLeader( int team ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam != team) continue; if (level.clients[i].sess.teamLeader) break; } if (i >= level.maxclients) { for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam != team) continue; if (!(g_entities[i].r.svFlags & SVF_BOT)) { level.clients[i].sess.teamLeader = qtrue; break; } } for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam != team) continue; level.clients[i].sess.teamLeader = qtrue; break; } } } /* ================== CheckTeamVote ================== */ void CheckTeamVote( int team ) { int cs_offset; if ( team == TEAM_RED ) cs_offset = 0; else if ( team == TEAM_BLUE ) cs_offset = 1; else return; if ( !level.teamVoteTime[cs_offset] ) { return; } if ( level.time - level.teamVoteTime[cs_offset] >= VOTE_TIME ) { trap_SendServerCommand( -1, "print \"Team vote failed.\n\"" ); } else { if ( level.teamVoteYes[cs_offset] > level.numteamVotingClients[cs_offset]/2 ) { // execute the command, then remove the vote trap_SendServerCommand( -1, "print \"Team vote passed.\n\"" ); // if ( !Q_strncmp( "leader", level.teamVoteString[cs_offset], 6) ) { //set the team leader SetLeader(team, atoi(level.teamVoteString[cs_offset] + 7)); } else { trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.teamVoteString[cs_offset] ) ); } } else if ( level.teamVoteNo[cs_offset] >= level.numteamVotingClients[cs_offset]/2 ) { // same behavior as a timeout trap_SendServerCommand( -1, "print \"Team vote failed.\n\"" ); } else { // still waiting for a majority return; } } level.teamVoteTime[cs_offset] = 0; trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" ); } /* ================== CheckCvars ================== */ void CheckCvars( void ) { static int lastMod = -1; if ( g_password.modificationCount != lastMod ) { lastMod = g_password.modificationCount; if ( *g_password.string && Q_stricmp( g_password.string, "none" ) ) { trap_Cvar_Set( "g_needpass", "1" ); } else { trap_Cvar_Set( "g_needpass", "0" ); } } } /* ============= G_RunThink Runs thinking code for this frame if necessary ============= */ void G_RunThink (gentity_t *ent) { float thinktime; thinktime = ent->nextthink; if (thinktime <= 0) { return; } if (thinktime > level.time) { return; } ent->nextthink = 0; if (!ent->think) { G_Error ( "NULL ent->think"); } ent->think (ent); } /* ================ G_RunFrame Advances the non-player objects in the world ================ */ void G_RunFrame( int levelTime ) { int i; gentity_t *ent; int msec; int start, end; // if we are waiting for the level to restart, do nothing if ( level.restarted ) { return; } level.framenum++; level.previousTime = level.time; level.time = levelTime; msec = level.time - level.previousTime; // get any cvar changes G_UpdateCvars(); if( (g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && !(g_elimflags.integer & EF_NO_FREESPEC) && g_elimination_lockspectator.integer>1) trap_Cvar_Set("elimflags",va("%i",g_elimflags.integer|EF_NO_FREESPEC)); else if( (g_elimflags.integer & EF_NO_FREESPEC) && g_elimination_lockspectator.integer<2) trap_Cvar_Set("elimflags",va("%i",g_elimflags.integer&(~EF_NO_FREESPEC) ) ); if( g_elimination_ctf_oneway.integer && !(g_elimflags.integer & EF_ONEWAY) ) { trap_Cvar_Set("elimflags",va("%i",g_elimflags.integer|EF_ONEWAY ) ); //If the server admin has enabled it midgame imidiantly braodcast attacking team SendAttackingTeamMessageToAllClients(); } else if( !g_elimination_ctf_oneway.integer && (g_elimflags.integer & EF_ONEWAY) ) { trap_Cvar_Set("elimflags",va("%i",g_elimflags.integer&(~EF_ONEWAY) ) ); } // // go through all allocated objects // start = trap_Milliseconds(); ent = &g_entities[0]; for (i=0 ; iinuse ) { continue; } // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if ( ent->client ) { ent->client->ps.externalEvent = 0; // predicted events should never be set to zero //ent->client->ps.events[0] = 0; //ent->client->ps.events[1] = 0; } } if ( ent->freeAfterEvent ) { // tempEntities or dropped items completely go away after their event G_FreeEntity( ent ); continue; } else if ( ent->unlinkAfterEvent ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent = qfalse; trap_UnlinkEntity( ent ); } } // temporary entities don't think if ( ent->freeAfterEvent ) { continue; } if ( !ent->r.linked && ent->neverFree ) { continue; } //unlagged - backward reconciliation #2 // we'll run missiles separately to save CPU in backward reconciliation /* if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); continue; } */ //unlagged - backward reconciliation #2 if ( ent->s.eType == ET_ITEM || ent->physicsObject ) { G_RunItem( ent ); continue; } if ( ent->s.eType == ET_MOVER ) { G_RunMover( ent ); continue; } if ( i < MAX_CLIENTS ) { G_RunClient( ent ); continue; } G_RunThink( ent ); } //unlagged - backward reconciliation #2 // NOW run the missiles, with all players backward-reconciled // to the positions they were in exactly 50ms ago, at the end // of the last server frame G_TimeShiftAllClients( level.previousTime, NULL ); ent = &g_entities[0]; for (i=0 ; iinuse ) { continue; } // temporary entities don't think if ( ent->freeAfterEvent ) { continue; } if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); } } G_UnTimeShiftAllClients( NULL ); //unlagged - backward reconciliation #2 end = trap_Milliseconds(); start = trap_Milliseconds(); // perform final fixups on the players ent = &g_entities[0]; for (i=0 ; i < level.maxclients ; i++, ent++ ) { if ( ent->inuse ) { ClientEndFrame( ent ); } } end = trap_Milliseconds(); // see if it is time to do a tournement restart CheckTournament(); //Check Elimination state CheckElimination(); CheckLMS(); //Check Double Domination CheckDoubleDomination(); CheckDomination(); //Sago: I just need to think why I placed this here... they should only spawn once if(g_gametype.integer == GT_DOMINATION) Team_Dom_SpawnPoints(); // see if it is time to end the level CheckExitRules(); // update to team status? CheckTeamStatus(); // cancel vote if timed out CheckVote(); // check team votes CheckTeamVote( TEAM_RED ); CheckTeamVote( TEAM_BLUE ); // for tracking changes CheckCvars(); if (g_listEntity.integer) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } trap_Cvar_Set("g_listEntity", "0"); } //unlagged - backward reconciliation #4 // record the time at the end of this frame - it should be about // the time the next frame begins - when the server starts // accepting commands from connected clients level.frameStartTime = trap_Milliseconds(); //unlagged - backward reconciliation #4 } openarena_0.8.8.orig/code/game/g_items.c0000644000175000017500000007334211656310264016655 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" /* Items are any object that a player can touch to gain some effect. Pickup will return the number of seconds until they should respawn. all items should pop when dropped in lava or slime Respawnable items don't actually go away when picked up, they are just made invisible and untouchable. This allows them to ride movers and respawn apropriately. */ #define RESPAWN_ARMOR 25 #define RESPAWN_HEALTH 35 #define RESPAWN_AMMO 40 #define RESPAWN_HOLDABLE 60 #define RESPAWN_MEGAHEALTH 35//120 #define RESPAWN_POWERUP 120 //====================================================================== int Pickup_Powerup( gentity_t *ent, gentity_t *other ) { int quantity; int i; gclient_t *client; if ( !other->client->ps.powerups[ent->item->giTag] ) { // round timing to seconds to make multiple powerup timers // count in sync other->client->ps.powerups[ent->item->giTag] = level.time - ( level.time % 1000 ); } if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } other->client->ps.powerups[ent->item->giTag] += quantity * 1000; // give any nearby players a "denied" anti-reward for ( i = 0 ; i < level.maxclients ; i++ ) { vec3_t delta; float len; vec3_t forward; trace_t tr; client = &level.clients[i]; if ( client == other->client ) { continue; } if ( client->pers.connected == CON_DISCONNECTED ) { continue; } if ( client->ps.stats[STAT_HEALTH] <= 0 ) { continue; } // if same team in team game, no sound // cannot use OnSameTeam as it expects to g_entities, not clients if ( g_gametype.integer >= GT_TEAM && g_ffa_gt==0 && other->client->sess.sessionTeam == client->sess.sessionTeam ) { continue; } // if too far away, no sound VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta ); len = VectorNormalize( delta ); if ( len > 192 ) { continue; } // if not facing, no sound AngleVectors( client->ps.viewangles, forward, NULL, NULL ); if ( DotProduct( delta, forward ) < 0.4 ) { continue; } // if not line of sight, no sound trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID ); if ( tr.fraction != 1.0 ) { continue; } // anti-reward client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD; } return RESPAWN_POWERUP; } //====================================================================== int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) { int clientNum; char userinfo[MAX_INFO_STRING]; float handicap; int max; other->client->ps.stats[STAT_PERSISTANT_POWERUP] = ent->item - bg_itemlist; other->client->persistantPowerup = ent; switch( ent->item->giTag ) { case PW_GUARD: clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } max = (int)(2 * handicap); other->health = max; other->client->ps.stats[STAT_HEALTH] = max; other->client->ps.stats[STAT_MAX_HEALTH] = max; other->client->ps.stats[STAT_ARMOR] = max; other->client->pers.maxHealth = max; break; case PW_SCOUT: clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; other->client->ps.stats[STAT_ARMOR] = 0; break; case PW_DOUBLER: clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; break; case PW_AMMOREGEN: clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; memset(other->client->ammoTimes, 0, sizeof(other->client->ammoTimes)); break; default: clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; break; } return -1; } //====================================================================== int Pickup_Holdable( gentity_t *ent, gentity_t *other ) { other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist; if( ent->item->giTag == HI_KAMIKAZE ) { other->client->ps.eFlags |= EF_KAMIKAZE; } return RESPAWN_HOLDABLE; } //====================================================================== void Add_Ammo (gentity_t *ent, int weapon, int count) { ent->client->ps.ammo[weapon] += count; if ( ent->client->ps.ammo[weapon] > 200 ) { ent->client->ps.ammo[weapon] = 200; } } int Pickup_Ammo (gentity_t *ent, gentity_t *other) { int quantity; if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } Add_Ammo (other, ent->item->giTag, quantity); return RESPAWN_AMMO; } //====================================================================== int Pickup_Weapon (gentity_t *ent, gentity_t *other) { int quantity; if ( ent->count < 0 ) { quantity = 0; // None for you, sir! } else { if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } // dropped items and teamplay weapons always have full ammo if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) { // respawning rules // drop the quantity if the already have over the minimum if ( other->client->ps.ammo[ ent->item->giTag ] < quantity ) { quantity = quantity - other->client->ps.ammo[ ent->item->giTag ]; } else { quantity = 1; // only add a single shot } } } // add the weapon other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag ); Add_Ammo( other, ent->item->giTag, quantity ); if (ent->item->giTag == WP_GRAPPLING_HOOK) other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo // team deathmatch has slow weapon respawns if ( g_gametype.integer == GT_TEAM ) { return g_weaponTeamRespawn.integer; } return g_weaponRespawn.integer; } //====================================================================== int Pickup_Health (gentity_t *ent, gentity_t *other) { int max; int quantity; if( !other->client) return RESPAWN_HEALTH; // small and mega healths will go over the max if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max = other->client->ps.stats[STAT_MAX_HEALTH]; } else if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) { max = other->client->ps.stats[STAT_MAX_HEALTH]; } else { max = other->client->ps.stats[STAT_MAX_HEALTH] * 2; } if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } other->health += quantity; if (other->health > max ) { other->health = max; } other->client->ps.stats[STAT_HEALTH] = other->health; if ( ent->item->quantity == 100 ) { // mega health respawns slow return RESPAWN_MEGAHEALTH; } return RESPAWN_HEALTH; } //====================================================================== int Pickup_Armor( gentity_t *ent, gentity_t *other ) { int upperBound; other->client->ps.stats[STAT_ARMOR] += ent->item->quantity; if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { upperBound = other->client->ps.stats[STAT_MAX_HEALTH]; } else { upperBound = other->client->ps.stats[STAT_MAX_HEALTH] * 2; } if ( other->client->ps.stats[STAT_ARMOR] > upperBound ) { other->client->ps.stats[STAT_ARMOR] = upperBound; } return RESPAWN_ARMOR; } //====================================================================== /* =============== RespawnItem =============== */ void RespawnItem( gentity_t *ent ) { //Don't spawn quad if quadfactor are 1.0 or less if(ent->item->giType == IT_POWERUP && ent->item->giTag == PW_QUAD && g_quadfactor.value <= 1.0) return; // randomly select from teamed entities if (ent->team) { gentity_t *master; int count; int choice; if ( !ent->teammaster ) { G_Error( "RespawnItem: bad teammaster"); } master = ent->teammaster; for (count = 0, ent = master; ent; ent = ent->teamchain, count++) ; choice = (count > 0)? rand()%count : 0; for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++) ; } ent->r.contents = CONTENTS_TRIGGER; ent->s.eFlags &= ~EF_NODRAW; ent->r.svFlags &= ~SVF_NOCLIENT; trap_LinkEntity (ent); if ( ent->item->giType == IT_POWERUP ) { // play powerup spawn sound to all clients gentity_t *te; // if the powerup respawn sound should Not be global if (ent->speed) { te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND ); } else { te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND ); } te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" ); te->r.svFlags |= SVF_BROADCAST; } if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == HI_KAMIKAZE ) { // play powerup spawn sound to all clients gentity_t *te; // if the powerup respawn sound should Not be global if (ent->speed) { te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND ); } else { te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND ); } te->s.eventParm = G_SoundIndex( "sound/items/kamikazerespawn.wav" ); te->r.svFlags |= SVF_BROADCAST; } // play the normal respawn sound only to nearby clients G_AddEvent( ent, EV_ITEM_RESPAWN, 0 ); ent->nextthink = 0; } /* =============== Touch_Item =============== */ void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) { int respawn; qboolean predict; //instant gib if ((g_instantgib.integer || g_rockets.integer || g_gametype.integer == GT_CTF_ELIMINATION || g_elimination_allgametypes.integer) && ent->item->giType != IT_TEAM) return; //Cannot touch flag before round starts if(g_gametype.integer == GT_CTF_ELIMINATION && level.roundNumber != level.roundNumberStarted) return; //Cannot take ctf elimination oneway if(g_gametype.integer == GT_CTF_ELIMINATION && g_elimination_ctf_oneway.integer!=0 && ( (other->client->sess.sessionTeam==TEAM_BLUE && (level.eliminationSides+level.roundNumber)%2 == 0 ) || (other->client->sess.sessionTeam==TEAM_RED && (level.eliminationSides+level.roundNumber)%2 != 0 ) )) return; if (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_LMS) return; //nothing to pick up in elimination if (!other->client) return; if (other->health < 1) return; // dead people can't pickup // the same pickup rules are used for client side and server side if ( !BG_CanItemBeGrabbed( g_gametype.integer, &ent->s, &other->client->ps ) ) { return; } //In double DD we cannot "pick up" a flag we already got if(g_gametype.integer == GT_DOUBLE_D) { if( strcmp(ent->classname, "team_CTF_redflag") == 0 ) if(other->client->sess.sessionTeam == level.pointStatusA) return; if( strcmp(ent->classname, "team_CTF_blueflag") == 0 ) if(other->client->sess.sessionTeam == level.pointStatusB) return; } G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname ); predict = other->client->pers.predictItemPickup; // call the item-specific pickup function switch( ent->item->giType ) { case IT_WEAPON: respawn = Pickup_Weapon(ent, other); // predict = qfalse; break; case IT_AMMO: respawn = Pickup_Ammo(ent, other); // predict = qfalse; break; case IT_ARMOR: respawn = Pickup_Armor(ent, other); break; case IT_HEALTH: respawn = Pickup_Health(ent, other); break; case IT_POWERUP: respawn = Pickup_Powerup(ent, other); predict = qfalse; break; case IT_PERSISTANT_POWERUP: respawn = Pickup_PersistantPowerup(ent, other); break; case IT_TEAM: respawn = Pickup_Team(ent, other); //If touching a team item remove spawnprotection if(other->client->spawnprotected) other->client->spawnprotected = qfalse; break; case IT_HOLDABLE: respawn = Pickup_Holdable(ent, other); break; default: return; } if ( !respawn ) { return; } // play the normal pickup sound if (predict) { G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.modelindex ); } else { G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex ); } // powerup pickups are global broadcasts if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) { // if we want the global sound to play if (!ent->speed) { gentity_t *te; te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP ); te->s.eventParm = ent->s.modelindex; te->r.svFlags |= SVF_BROADCAST; } else { gentity_t *te; te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP ); te->s.eventParm = ent->s.modelindex; // only send this temp entity to a single client te->r.svFlags |= SVF_SINGLECLIENT; te->r.singleClient = other->s.number; } } // fire item targets G_UseTargets (ent, other); // wait of -1 will not respawn if ( ent->wait == -1 ) { ent->r.svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; ent->unlinkAfterEvent = qtrue; return; } // non zero wait overrides respawn time if ( ent->wait ) { respawn = ent->wait; } // random can be used to vary the respawn time if ( ent->random ) { respawn += crandom() * ent->random; if ( respawn < 1 ) { respawn = 1; } } // dropped items will not respawn if ( ent->flags & FL_DROPPED_ITEM ) { ent->freeAfterEvent = qtrue; } // picked up items still stay around, they just don't // draw anything. This allows respawnable items // to be placed on movers. ent->r.svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; // ZOID // A negative respawn times means to never respawn this item (but don't // delete it). This is used by items that are respawned by third party // events such as ctf flags if ( respawn <= 0 ) { ent->nextthink = 0; ent->think = 0; } else { ent->nextthink = level.time + respawn * 1000; ent->think = RespawnItem; } trap_LinkEntity( ent ); } //====================================================================== /* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) { gentity_t *dropped; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = item->classname; dropped->item = item; VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS); VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); dropped->r.contents = CONTENTS_TRIGGER; dropped->touch = Touch_Item; G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); dropped->s.eFlags |= EF_BOUNCE_HALF; if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_DOUBLE_D) && item->giType == IT_TEAM) { // Special case for CTF flags dropped->think = Team_DroppedFlagThink; dropped->nextthink = level.time + 30000; Team_CheckDroppedItem( dropped ); } else { // auto-remove after 30 seconds dropped->think = G_FreeEntity; dropped->nextthink = level.time + 30000; } dropped->flags = FL_DROPPED_ITEM; trap_LinkEntity (dropped); return dropped; } /* ================ Drop_Item Spawns an item and tosses it forward ================ */ gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) { vec3_t velocity; vec3_t angles; VectorCopy( ent->s.apos.trBase, angles ); angles[YAW] += angle; angles[PITCH] = 0; // always forward AngleVectors( angles, velocity, NULL, NULL ); VectorScale( velocity, 150, velocity ); velocity[2] += 200 + crandom() * 50; return LaunchItem( item, ent->s.pos.trBase, velocity ); } /* ================ Use_Item Respawn the item ================ */ void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) { RespawnItem( ent ); } //====================================================================== /* ================ FinishSpawningItem Traces down to find where an item should rest, instead of letting them free fall from their spawn points ================ */ void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS ); VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item ent->r.contents = CONTENTS_TRIGGER; ent->touch = Touch_Item; // useing an item causes it to respawn ent->use = Use_Item; if ( ent->spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID ); if ( tr.startsolid ) { G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEntity( ent ); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } // team slaves and targeted items aren't present at start if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) { ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; return; } // powerups don't spawn in for a while (but not in elimination) if(g_gametype.integer != GT_ELIMINATION && g_gametype.integer != GT_CTF_ELIMINATION && g_gametype.integer != GT_LMS && !g_instantgib.integer && !g_elimination_allgametypes.integer && !g_rockets.integer ) if ( ent->item->giType == IT_POWERUP ) { float respawn; respawn = 45 + crandom() * 15; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; ent->nextthink = level.time + respawn * 1000; ent->think = RespawnItem; return; } trap_LinkEntity (ent); } qboolean itemRegistered[MAX_ITEMS]; /* ================== G_CheckTeamItems ================== */ void G_CheckTeamItems( void ) { // Set up team stuff Team_InitGame(); if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_DOUBLE_D) { gitem_t *item; // check for the two flags item = BG_FindItem( "Red Flag" ); if ( !item || !itemRegistered[ item - bg_itemlist ] ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map\n" ); } item = BG_FindItem( "Blue Flag" ); if ( !item || !itemRegistered[ item - bg_itemlist ] ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map\n" ); } } if( g_gametype.integer == GT_1FCTF ) { gitem_t *item; // check for all three flags item = BG_FindItem( "Red Flag" ); if ( !item || !itemRegistered[ item - bg_itemlist ] ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map\n" ); } item = BG_FindItem( "Blue Flag" ); if ( !item || !itemRegistered[ item - bg_itemlist ] ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map\n" ); } item = BG_FindItem( "Neutral Flag" ); if ( !item || !itemRegistered[ item - bg_itemlist ] ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_neutralflag in map\n" ); } } if( g_gametype.integer == GT_OBELISK ) { gentity_t *ent; // check for the two obelisks ent = NULL; ent = G_Find( ent, FOFS(classname), "team_redobelisk" ); if( !ent ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map\n" ); } ent = NULL; ent = G_Find( ent, FOFS(classname), "team_blueobelisk" ); if( !ent ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map\n" ); } } if( g_gametype.integer == GT_HARVESTER ) { gentity_t *ent; // check for all three obelisks ent = NULL; ent = G_Find( ent, FOFS(classname), "team_redobelisk" ); if( !ent ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map\n" ); } ent = NULL; ent = G_Find( ent, FOFS(classname), "team_blueobelisk" ); if( !ent ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map\n" ); } ent = NULL; ent = G_Find( ent, FOFS(classname), "team_neutralobelisk" ); if( !ent ) { G_Printf( S_COLOR_YELLOW "WARNING: No team_neutralobelisk in map\n" ); } } } /* ============== ClearRegisteredItems ============== */ void ClearRegisteredItems( void ) { memset( itemRegistered, 0, sizeof( itemRegistered ) ); if(g_instantgib.integer) { if(g_instantgib.integer & 2) RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) ); //RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_RAILGUN ) ); } else if(g_rockets.integer) { //RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) ); //RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_ROCKET_LAUNCHER ) ); } else { // players always start with the base weapon RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) ); if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS || g_elimination_allgametypes.integer) { RegisterItem( BG_FindItemForWeapon( WP_SHOTGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_GRENADE_LAUNCHER ) ); RegisterItem( BG_FindItemForWeapon( WP_ROCKET_LAUNCHER ) ); RegisterItem( BG_FindItemForWeapon( WP_LIGHTNING ) ); RegisterItem( BG_FindItemForWeapon( WP_RAILGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_PLASMAGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_BFG ) ); RegisterItem( BG_FindItemForWeapon( WP_NAILGUN ) ); RegisterItem( BG_FindItemForWeapon( WP_PROX_LAUNCHER ) ); RegisterItem( BG_FindItemForWeapon( WP_CHAINGUN ) ); } } if( g_gametype.integer == GT_HARVESTER ) { RegisterItem( BG_FindItem( "Red Cube" ) ); RegisterItem( BG_FindItem( "Blue Cube" ) ); } if(g_gametype.integer == GT_DOUBLE_D ) { RegisterItem( BG_FindItem( "Point A (Blue)" ) ); RegisterItem( BG_FindItem( "Point A (Red)" ) ); RegisterItem( BG_FindItem( "Point A (White)" ) ); RegisterItem( BG_FindItem( "Point B (Blue)" ) ); RegisterItem( BG_FindItem( "Point B (Red)" ) ); RegisterItem( BG_FindItem( "Point B (White)" ) ); } if(g_gametype.integer == GT_DOMINATION ) { RegisterItem( BG_FindItem( "Neutral domination point" ) ); RegisterItem( BG_FindItem( "Red domination point" ) ); RegisterItem( BG_FindItem( "Blue domination point" ) ); } } /* =============== RegisterItem The item will be added to the precache list =============== */ void RegisterItem( gitem_t *item ) { if ( !item ) { G_Error( "RegisterItem: NULL" ); } itemRegistered[ item - bg_itemlist ] = qtrue; } /* =============== SaveRegisteredItems Write the needed items to a config string so the client will know which ones to precache =============== */ void SaveRegisteredItems( void ) { char string[MAX_ITEMS+1]; int i; int count; count = 0; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( itemRegistered[i] ) { count++; string[i] = '1'; } else { string[i] = '0'; } } string[ bg_numItems ] = 0; G_Printf( "%i items registered\n", count ); trap_SetConfigstring(CS_ITEMS, string); } /* ============ G_ItemDisabled ============ */ int G_ItemDisabled( gitem_t *item ) { char name[128]; Com_sprintf(name, sizeof(name), "disable_%s", item->classname); return trap_Cvar_VariableIntegerValue( name ); } /* ============ G_SpawnItem Sets the clipping size and plants the object on the floor. Items can't be immediately dropped to floor, because they might be on an entity that hasn't spawned yet. ============ */ void G_SpawnItem (gentity_t *ent, gitem_t *item) { G_SpawnFloat( "random", "0", &ent->random ); G_SpawnFloat( "wait", "0", &ent->wait ); if((item->giType == IT_TEAM && (g_instantgib.integer || g_rockets.integer) ) || (!g_instantgib.integer && !g_rockets.integer) ) { //Don't load pickups in Elimination (or maybe... gives warnings) if (g_gametype.integer != GT_ELIMINATION && g_gametype.integer != GT_CTF_ELIMINATION && g_gametype.integer != GT_LMS) RegisterItem( item ); //Registrer flags anyway in CTF Elimination: if (g_gametype.integer == GT_CTF_ELIMINATION && item->giType == IT_TEAM) RegisterItem( item ); if ( G_ItemDisabled(item) ) return; } if(!g_persistantpowerups.integer && item->giType == IT_PERSISTANT_POWERUP) return; ent->item = item; // some movers spawn on the second frame, so delay item // spawns until the third frame so they can ride trains ent->nextthink = level.time + FRAMETIME * 2; ent->think = FinishSpawningItem; ent->physicsBounce = 0.50; // items are bouncy if (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_LMS || ( item->giType != IT_TEAM && (g_instantgib.integer || g_rockets.integer || g_elimination_allgametypes.integer || g_gametype.integer==GT_CTF_ELIMINATION) ) ) { ent->s.eFlags |= EF_NODRAW; //Invisible in elimination ent->r.svFlags |= SVF_NOCLIENT; //Don't broadcast } if(g_gametype.integer == GT_DOUBLE_D && (strcmp(ent->classname, "team_CTF_redflag")==0 || strcmp(ent->classname, "team_CTF_blueflag")==0 || strcmp(ent->classname, "team_CTF_neutralflag") == 0 || item->giType == IT_PERSISTANT_POWERUP )) ent->s.eFlags |= EF_NODRAW; //Don't draw the flag models/persistant powerups if( g_gametype.integer != GT_1FCTF && strcmp(ent->classname, "team_CTF_neutralflag") == 0) ent->s.eFlags |= EF_NODRAW; // Don't draw the flag in CTF_elimination if(strcmp(ent->classname, "domination_point") == 0) ent->s.eFlags |= EF_NODRAW; // Don't draw domination_point. It is just a pointer to where the Domination points should be placed if ( item->giType == IT_POWERUP ) { G_SoundIndex( "sound/items/poweruprespawn.wav" ); G_SpawnFloat( "noglobalsound", "0", &ent->speed); } if ( item->giType == IT_PERSISTANT_POWERUP ) { ent->s.generic1 = ent->spawnflags; } } /* ================ G_BounceItem ================ */ void G_BounceItem( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); // cut the velocity to keep from bouncing forever VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta ); // check for stop if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) { trace->endpos[2] += 1.0; // make sure it is off ground SnapVector( trace->endpos ); G_SetOrigin( ent, trace->endpos ); ent->s.groundEntityNum = trace->entityNum; return; } VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin); VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } /* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == -1 ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); return; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID; } trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask ); VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } trap_LinkEntity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { if (ent->item && ent->item->giType == IT_TEAM) { Team_FreeEntity(ent); } else { G_FreeEntity( ent ); } return; } G_BounceItem( ent, &tr ); } openarena_0.8.8.orig/code/game/g_spawn.c0000644000175000017500000004357111656310264016665 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" qboolean G_SpawnString( const char *key, const char *defaultString, char **out ) { int i; if ( !level.spawning ) { *out = (char *)defaultString; // G_Error( "G_SpawnString() called while not spawning" ); } for ( i = 0 ; i < level.numSpawnVars ; i++ ) { if ( !Q_stricmp( key, level.spawnVars[i][0] ) ) { *out = level.spawnVars[i][1]; return qtrue; } } *out = (char *)defaultString; return qfalse; } qboolean G_SpawnFloat( const char *key, const char *defaultString, float *out ) { char *s; qboolean present; present = G_SpawnString( key, defaultString, &s ); *out = atof( s ); return present; } qboolean G_SpawnInt( const char *key, const char *defaultString, int *out ) { char *s; qboolean present; present = G_SpawnString( key, defaultString, &s ); *out = atoi( s ); return present; } qboolean G_SpawnVector( const char *key, const char *defaultString, float *out ) { char *s; qboolean present; present = G_SpawnString( key, defaultString, &s ); sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] ); return present; } // // fields are needed for spawning from the entity string // typedef enum { F_INT, F_FLOAT, F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL F_GSTRING, // string on disk, pointer in memory, TAG_GAME F_VECTOR, F_ANGLEHACK, F_ENTITY, // index on disk, pointer in memory F_ITEM, // index on disk, pointer in memory F_CLIENT, // index on disk, pointer in memory F_IGNORE } fieldtype_t; typedef struct { char *name; int ofs; fieldtype_t type; // int flags; } field_t; field_t fields[] = { {"classname", FOFS(classname), F_LSTRING}, {"origin", FOFS(s.origin), F_VECTOR}, {"model", FOFS(model), F_LSTRING}, {"model2", FOFS(model2), F_LSTRING}, {"spawnflags", FOFS(spawnflags), F_INT}, {"speed", FOFS(speed), F_FLOAT}, {"target", FOFS(target), F_LSTRING}, {"targetname", FOFS(targetname), F_LSTRING}, {"message", FOFS(message), F_LSTRING}, {"team", FOFS(team), F_LSTRING}, {"wait", FOFS(wait), F_FLOAT}, {"random", FOFS(random), F_FLOAT}, {"count", FOFS(count), F_INT}, {"health", FOFS(health), F_INT}, {"light", 0, F_IGNORE}, {"dmg", FOFS(damage), F_INT}, {"angles", FOFS(s.angles), F_VECTOR}, {"angle", FOFS(s.angles), F_ANGLEHACK}, {"targetShaderName", FOFS(targetShaderName), F_LSTRING}, {"targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING}, {NULL} }; typedef struct { char *name; void (*spawn)(gentity_t *ent); } spawn_t; void SP_info_player_start (gentity_t *ent); void SP_info_player_deathmatch (gentity_t *ent); void SP_info_player_intermission (gentity_t *ent); //For Double Domination: void SP_info_player_dd (gentity_t *ent); void SP_info_player_dd_red (gentity_t *ent); void SP_info_player_dd_blue (gentity_t *ent); //standard domination: void SP_domination_point ( gentity_t *ent); void SP_info_firstplace(gentity_t *ent); void SP_info_secondplace(gentity_t *ent); void SP_info_thirdplace(gentity_t *ent); void SP_info_podium(gentity_t *ent); void SP_func_plat (gentity_t *ent); void SP_func_static (gentity_t *ent); void SP_func_rotating (gentity_t *ent); void SP_func_bobbing (gentity_t *ent); void SP_func_pendulum( gentity_t *ent ); void SP_func_button (gentity_t *ent); void SP_func_door (gentity_t *ent); void SP_func_train (gentity_t *ent); void SP_func_timer (gentity_t *self); void SP_trigger_always (gentity_t *ent); void SP_trigger_multiple (gentity_t *ent); void SP_trigger_push (gentity_t *ent); void SP_trigger_teleport (gentity_t *ent); void SP_trigger_hurt (gentity_t *ent); void SP_target_remove_powerups( gentity_t *ent ); void SP_target_give (gentity_t *ent); void SP_target_delay (gentity_t *ent); void SP_target_speaker (gentity_t *ent); void SP_target_print (gentity_t *ent); void SP_target_laser (gentity_t *self); void SP_target_character (gentity_t *ent); void SP_target_score( gentity_t *ent ); void SP_target_teleporter( gentity_t *ent ); void SP_target_relay (gentity_t *ent); void SP_target_kill (gentity_t *ent); void SP_target_position (gentity_t *ent); void SP_target_location (gentity_t *ent); void SP_target_push (gentity_t *ent); void SP_light (gentity_t *self); void SP_info_null (gentity_t *self); void SP_info_notnull (gentity_t *self); void SP_info_camp (gentity_t *self); void SP_path_corner (gentity_t *self); void SP_misc_teleporter_dest (gentity_t *self); void SP_misc_model(gentity_t *ent); void SP_misc_portal_camera(gentity_t *ent); void SP_misc_portal_surface(gentity_t *ent); void SP_shooter_rocket( gentity_t *ent ); void SP_shooter_plasma( gentity_t *ent ); void SP_shooter_grenade( gentity_t *ent ); void SP_team_CTF_redplayer( gentity_t *ent ); void SP_team_CTF_blueplayer( gentity_t *ent ); void SP_team_CTF_redspawn( gentity_t *ent ); void SP_team_CTF_bluespawn( gentity_t *ent ); void SP_team_blueobelisk( gentity_t *ent ); void SP_team_redobelisk( gentity_t *ent ); void SP_team_neutralobelisk( gentity_t *ent ); void SP_item_botroam( gentity_t *ent ) { } spawn_t spawns[] = { // info entities don't do anything at all, but provide positional // information for things controlled by other processes {"info_player_start", SP_info_player_start}, {"info_player_deathmatch", SP_info_player_deathmatch}, {"info_player_intermission", SP_info_player_intermission}, //Double Domination player spawn: {"info_player_dd", SP_info_player_dd}, {"info_player_dd_red", SP_info_player_dd_red}, {"info_player_dd_blue", SP_info_player_dd_blue}, //Standard Domination point spawn: {"domination_point", SP_domination_point}, {"info_null", SP_info_null}, {"info_notnull", SP_info_notnull}, // use target_position instead {"info_camp", SP_info_camp}, {"func_plat", SP_func_plat}, {"func_button", SP_func_button}, {"func_door", SP_func_door}, {"func_static", SP_func_static}, {"func_rotating", SP_func_rotating}, {"func_bobbing", SP_func_bobbing}, {"func_pendulum", SP_func_pendulum}, {"func_train", SP_func_train}, {"func_group", SP_info_null}, {"func_timer", SP_func_timer}, // rename trigger_timer? // Triggers are brush objects that cause an effect when contacted // by a living player, usually involving firing targets. // While almost everything could be done with // a single trigger class and different targets, triggered effects // could not be client side predicted (push and teleport). {"trigger_always", SP_trigger_always}, {"trigger_multiple", SP_trigger_multiple}, {"trigger_push", SP_trigger_push}, {"trigger_teleport", SP_trigger_teleport}, {"trigger_hurt", SP_trigger_hurt}, // targets perform no action by themselves, but must be triggered // by another entity {"target_give", SP_target_give}, {"target_remove_powerups", SP_target_remove_powerups}, {"target_delay", SP_target_delay}, {"target_speaker", SP_target_speaker}, {"target_print", SP_target_print}, {"target_laser", SP_target_laser}, {"target_score", SP_target_score}, {"target_teleporter", SP_target_teleporter}, {"target_relay", SP_target_relay}, {"target_kill", SP_target_kill}, {"target_position", SP_target_position}, {"target_location", SP_target_location}, {"target_push", SP_target_push}, {"light", SP_light}, {"path_corner", SP_path_corner}, {"misc_teleporter_dest", SP_misc_teleporter_dest}, {"misc_model", SP_misc_model}, {"misc_portal_surface", SP_misc_portal_surface}, {"misc_portal_camera", SP_misc_portal_camera}, {"shooter_rocket", SP_shooter_rocket}, {"shooter_grenade", SP_shooter_grenade}, {"shooter_plasma", SP_shooter_plasma}, {"team_CTF_redplayer", SP_team_CTF_redplayer}, {"team_CTF_blueplayer", SP_team_CTF_blueplayer}, {"team_CTF_redspawn", SP_team_CTF_redspawn}, {"team_CTF_bluespawn", SP_team_CTF_bluespawn}, {"team_redobelisk", SP_team_redobelisk}, {"team_blueobelisk", SP_team_blueobelisk}, {"team_neutralobelisk", SP_team_neutralobelisk}, {"item_botroam", SP_item_botroam}, {NULL, 0} }; /* =============== G_CallSpawn Finds the spawn function for the entity and calls it, returning qfalse if not found =============== */ qboolean G_CallSpawn( gentity_t *ent ) { spawn_t *s; gitem_t *item; char cvarname[128]; char itemname[128]; //Construct a replace cvar: Com_sprintf(cvarname, sizeof(cvarname), "replace_%s", ent->classname); //Look an alternative item up: trap_Cvar_VariableStringBuffer(cvarname,itemname,sizeof(itemname)); if(itemname[0]==0) //If nothing found use original Com_sprintf(itemname, sizeof(itemname), "%s", ent->classname); else G_Printf ("%s replaced by %s\n", ent->classname, itemname); if ( itemname[0]==0) { G_Printf ("G_CallSpawn: NULL classname\n"); return qfalse; } // check item spawn functions for ( item=bg_itemlist+1 ; item->classname ; item++ ) { if ( !strcmp(item->classname, itemname) ) { G_SpawnItem( ent, item ); return qtrue; } } // check normal spawn functions for ( s=spawns ; s->name ; s++ ) { if ( !strcmp(s->name, itemname) ) { // found it s->spawn(ent); return qtrue; } } G_Printf ("%s doesn't have a spawn function\n", itemname); return qfalse; } /* ============= G_NewString Builds a copy of the string, translating \n to real linefeeds so message texts can be multi-line ============= */ char *G_NewString( const char *string ) { char *newb, *new_p; int i,l; l = strlen(string) + 1; //KK-OAX Changed to Tremulous's BG_Alloc newb = BG_Alloc( l ); new_p = newb; // turn \n into a real linefeed 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 newb; } /* =============== G_ParseField Takes a key/value pair and sets the binary values in a gentity =============== */ void G_ParseField( const char *key, const char *value, gentity_t *ent ) { field_t *f; byte *b; float v; vec3_t vec; for ( f=fields ; f->name ; f++ ) { if ( !Q_stricmp(f->name, key) ) { // found it b = (byte *)ent; switch( f->type ) { case F_LSTRING: *(char **)(b+f->ofs) = G_NewString (value); break; case F_VECTOR: sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ((float *)(b+f->ofs))[0] = vec[0]; ((float *)(b+f->ofs))[1] = vec[1]; ((float *)(b+f->ofs))[2] = vec[2]; break; case F_INT: *(int *)(b+f->ofs) = atoi(value); break; case F_FLOAT: *(float *)(b+f->ofs) = atof(value); break; case F_ANGLEHACK: v = atof(value); ((float *)(b+f->ofs))[0] = 0; ((float *)(b+f->ofs))[1] = v; ((float *)(b+f->ofs))[2] = 0; break; default: case F_IGNORE: break; } return; } } } /* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i; gentity_t *ent; char *s, *value, *gametypeName; static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "elimination", "ctf", "lms", "dd", "dom"}; // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER) if ( g_gametype.integer >= GT_TEAM && !g_ffa_gt ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } #ifdef MISSIONPACK G_SpawnInt( "notta", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } #else G_SpawnInt( "notq3a", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } #endif if( G_SpawnString( "!gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( s ) { G_FreeEntity( ent ); return; } } } if( G_SpawnString( "gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( !s ) { G_FreeEntity( ent ); return; } } } // move editor origin to pos VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn( ent ) ) { G_FreeEntity( ent ); } } /* ==================== G_AddSpawnVarToken ==================== */ char *G_AddSpawnVarToken( const char *string ) { int l; char *dest; l = strlen( string ); if ( level.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS ) { G_Error( "G_AddSpawnVarToken: MAX_SPAWN_VARS" ); } dest = level.spawnVarChars + level.numSpawnVarChars; memcpy( dest, string, l+1 ); level.numSpawnVarChars += l + 1; return dest; } /* ==================== G_ParseSpawnVars Parses a brace bounded set of key / value pairs out of the level's entity strings into level.spawnVars[] This does not actually spawn an entity. ==================== */ qboolean G_ParseSpawnVars( void ) { char keyname[MAX_TOKEN_CHARS]; char com_token[MAX_TOKEN_CHARS]; level.numSpawnVars = 0; level.numSpawnVarChars = 0; // parse the opening brace if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { // end of spawn string return qfalse; } if ( com_token[0] != '{' ) { G_Error( "G_ParseSpawnVars: found %s when expecting {",com_token ); } // go through all the key / value pairs while ( 1 ) { // parse key if ( !trap_GetEntityToken( keyname, sizeof( keyname ) ) ) { G_Error( "G_ParseSpawnVars: EOF without closing brace" ); } if ( keyname[0] == '}' ) { break; } // parse value if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { G_Error( "G_ParseSpawnVars: EOF without closing brace" ); } if ( com_token[0] == '}' ) { G_Error( "G_ParseSpawnVars: closing brace without data" ); } if ( level.numSpawnVars == MAX_SPAWN_VARS ) { G_Error( "G_ParseSpawnVars: MAX_SPAWN_VARS" ); } level.spawnVars[ level.numSpawnVars ][0] = G_AddSpawnVarToken( keyname ); level.spawnVars[ level.numSpawnVars ][1] = G_AddSpawnVarToken( com_token ); level.numSpawnVars++; } return qtrue; } /*QUAKED worldspawn (0 0 0) ? Every map should have exactly one worldspawn. "music" music wav file "gravity" 800 is default gravity "message" Text to print during connection process */ void SP_worldspawn( void ) { char *s; G_SpawnString( "classname", "", &s ); if ( Q_stricmp( s, "worldspawn" ) ) { G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); } // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); trap_SetConfigstring( CS_LEVEL_START_TIME, va("%i", level.startTime ) ); if ( *g_music.string && Q_stricmp( g_music.string, "none" ) ) { trap_SetConfigstring( CS_MUSIC, g_music.string ); } else { G_SpawnString( "music", "", &s ); trap_SetConfigstring( CS_MUSIC, s ); } G_SpawnString( "message", "", &s ); trap_SetConfigstring( CS_MESSAGE, s ); // map specific message trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day G_SpawnString( "gravity", "800", &s ); trap_Cvar_Set( "g_gravity", s ); G_SpawnString( "enableDust", "0", &s ); trap_Cvar_Set( "g_enableDust", s ); G_SpawnString( "enableBreath", "0", &s ); trap_Cvar_Set( "g_enableBreath", s ); g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD; g_entities[ENTITYNUM_WORLD].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_WORLD].classname = "worldspawn"; g_entities[ENTITYNUM_NONE].s.number = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].classname = "nothing"; // see if we want a warmup time trap_SetConfigstring( CS_WARMUP, "" ); if ( g_restarted.integer ) { trap_Cvar_Set( "g_restarted", "0" ); level.warmupTime = 0; } else if ( g_doWarmup.integer ) { // Turn it on level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } } /* ============== G_SpawnEntitiesFromString Parses textual entity definitions out of an entstring and spawns gentities. ============== */ void G_SpawnEntitiesFromString( void ) { // allow calls to G_Spawn*() level.spawning = qtrue; level.numSpawnVars = 0; // the worldspawn is not an actual entity, but it still // has a "spawn" function to perform any global setup // needed by a level (setting configstrings or cvars, etc) if ( !G_ParseSpawnVars() ) { G_Error( "SpawnEntities: no entities" ); } SP_worldspawn(); // parse ents while( G_ParseSpawnVars() ) { G_SpawnGEntityFromSpawnVars(); } level.spawning = qfalse; // any future calls to G_Spawn*() will be errors } openarena_0.8.8.orig/code/game/match.h0000644000175000017500000001135111656310264016317 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // make sure this is the same character as we use in chats in g_cmd.c #define EC "\x19" //match template contexts #define MTCONTEXT_MISC 2 #define MTCONTEXT_INITIALTEAMCHAT 4 #define MTCONTEXT_TIME 8 #define MTCONTEXT_TEAMMATE 16 #define MTCONTEXT_ADDRESSEE 32 #define MTCONTEXT_PATROLKEYAREA 64 #define MTCONTEXT_REPLYCHAT 128 #define MTCONTEXT_CTF 256 #define MTCONTEXT_DD 512 //message types #define MSG_NEWLEADER 1 //new leader #define MSG_ENTERGAME 2 //enter game message #define MSG_HELP 3 //help someone #define MSG_ACCOMPANY 4 //accompany someone #define MSG_DEFENDKEYAREA 5 //defend a key area #define MSG_RUSHBASE 6 //everyone rush to base #define MSG_GETFLAG 7 //get the enemy flag #define MSG_STARTTEAMLEADERSHIP 8 //someone wants to become the team leader #define MSG_STOPTEAMLEADERSHIP 9 //someone wants to stop being the team leader #define MSG_WHOISTEAMLAEDER 10 //who is the team leader #define MSG_WAIT 11 //wait for someone #define MSG_WHATAREYOUDOING 12 //what are you doing? #define MSG_JOINSUBTEAM 13 //join a sub-team #define MSG_LEAVESUBTEAM 14 //leave a sub-team #define MSG_CREATENEWFORMATION 15 //create a new formation #define MSG_FORMATIONPOSITION 16 //tell someone his/her position in a formation #define MSG_FORMATIONSPACE 17 //set the formation intervening space #define MSG_DOFORMATION 18 //form a known formation #define MSG_DISMISS 19 //dismiss commanded team mates #define MSG_CAMP 20 //camp somewhere #define MSG_CHECKPOINT 21 //remember a check point #define MSG_PATROL 22 //patrol between certain keypoints #define MSG_LEADTHEWAY 23 //lead the way #define MSG_GETITEM 24 //get an item #define MSG_KILL 25 //kill someone #define MSG_WHEREAREYOU 26 //where is someone #define MSG_RETURNFLAG 27 //return the flag #define MSG_WHATISMYCOMMAND 28 //ask the team leader what to do #define MSG_WHICHTEAM 29 //ask which team a bot is in #define MSG_TASKPREFERENCE 30 //tell your teamplay task preference #define MSG_ATTACKENEMYBASE 31 //attack the enemy base #define MSG_HARVEST 32 //go harvest #define MSG_SUICIDE 33 //order to suicide // //Double Domination messages #define MSG_TAKEA 90 #define MSG_TAKEB 91 // #define MSG_ME 100 #define MSG_EVERYONE 101 #define MSG_MULTIPLENAMES 102 #define MSG_NAME 103 #define MSG_PATROLKEYAREA 104 #define MSG_MINUTES 105 #define MSG_SECONDS 106 #define MSG_FOREVER 107 #define MSG_FORALONGTIME 108 #define MSG_FORAWHILE 109 // #define MSG_CHATALL 200 #define MSG_CHATTEAM 201 #define MSG_CHATTELL 202 // #define MSG_CTF 300 //ctf message //command sub types #define ST_SOMEWHERE 0 #define ST_NEARITEM 1 #define ST_ADDRESSED 2 #define ST_METER 4 #define ST_FEET 8 #define ST_TIME 16 #define ST_HERE 32 #define ST_THERE 64 #define ST_I 128 #define ST_MORE 256 #define ST_BACK 512 #define ST_REVERSE 1024 #define ST_SOMEONE 2048 #define ST_GOTFLAG 4096 #define ST_CAPTUREDFLAG 8192 #define ST_RETURNEDFLAG 16384 #define ST_TEAM 32768 #define ST_1FCTFGOTFLAG 65535 //ctf task preferences #define ST_DEFENDER 1 #define ST_ATTACKER 2 #define ST_ROAMER 4 //word replacement variables #define THE_ENEMY 7 #define THE_TEAM 7 //team message variables #define NETNAME 0 #define PLACE 1 #define FLAG 1 #define MESSAGE 2 #define ADDRESSEE 2 #define ITEM 3 #define TEAMMATE 4 #define TEAMNAME 4 #define ENEMY 4 #define KEYAREA 5 #define FORMATION 5 #define POSITION 5 #define NUMBER 5 #define TIME 6 #define NAME 6 #define MORE 6 openarena_0.8.8.orig/code/game/g_target.c0000644000175000017500000003115611656310264017017 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" //========================================================== /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8) Gives the activator all the items pointed to. */ void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) { gentity_t *t; trace_t trace; if ( !activator->client ) { return; } if ( !ent->target ) { return; } memset( &trace, 0, sizeof( trace ) ); t = NULL; while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) { if ( !t->item ) { continue; } Touch_Item( t, activator, &trace ); // make sure it isn't going to respawn or show any events t->nextthink = 0; trap_UnlinkEntity( t ); } } void SP_target_give( gentity_t *ent ) { ent->use = Use_Target_Give; } //========================================================== /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8) takes away all the activators powerups. Used to drop flight powerups into death puts. */ void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) { if( !activator->client ) { return; } if( activator->client->ps.powerups[PW_REDFLAG] ) { Team_ReturnFlag( TEAM_RED ); } else if( activator->client->ps.powerups[PW_BLUEFLAG] ) { Team_ReturnFlag( TEAM_BLUE ); } else if( activator->client->ps.powerups[PW_NEUTRALFLAG] ) { Team_ReturnFlag( TEAM_FREE ); } memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) ); } void SP_target_remove_powerups( gentity_t *ent ) { ent->use = Use_target_remove_powerups; } //========================================================== /*QUAKED target_delay (1 0 0) (-8 -8 -8) (8 8 8) "wait" seconds to pause before firing targets. "random" delay variance, total delay = delay +/- random seconds */ void Think_Target_Delay( gentity_t *ent ) { G_UseTargets( ent, ent->activator ); } void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) { ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; ent->think = Think_Target_Delay; ent->activator = activator; } void SP_target_delay( gentity_t *ent ) { // check delay for backwards compatability if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) { G_SpawnFloat( "wait", "1", &ent->wait ); } if ( !ent->wait ) { ent->wait = 1; } ent->use = Use_Target_Delay; } //========================================================== /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8) "count" number of points to add, default 1 The activator is given this many points. */ void Use_Target_Score (gentity_t *ent, gentity_t *other, gentity_t *activator) { AddScore( activator, ent->r.currentOrigin, ent->count ); } void SP_target_score( gentity_t *ent ) { if ( !ent->count ) { ent->count = 1; } ent->use = Use_Target_Score; } //========================================================== /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private "message" text to print If "private", only the activator gets the message. If no checks, all clients get the message. */ void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) { if ( activator->client && ( ent->spawnflags & 4 ) ) { trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message )); return; } if ( ent->spawnflags & 3 ) { if ( ent->spawnflags & 1 ) { G_TeamCommand( TEAM_RED, va("cp \"%s\"", ent->message) ); } if ( ent->spawnflags & 2 ) { G_TeamCommand( TEAM_BLUE, va("cp \"%s\"", ent->message) ); } return; } trap_SendServerCommand( -1, va("cp \"%s\"", ent->message )); } void SP_target_print( gentity_t *ent ) { ent->use = Use_Target_Print; } //========================================================== /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator "noise" wav file to play A global sound will play full volume throughout the level. Activator sounds will play on the player that activated the target. Global and activator sounds can't be combined with looping. Normal sounds play each time the target is used. Looped sounds will be toggled by use functions. Multiple identical looping sounds will just increase volume without any speed cost. "wait" : Seconds between auto triggerings, 0 = don't auto trigger "random" wait variance, default is 0 */ void Use_Target_Speaker (gentity_t *ent, gentity_t *other, gentity_t *activator) { if (ent->spawnflags & 3) { // looping sound toggles if (ent->s.loopSound) ent->s.loopSound = 0; // turn it off else ent->s.loopSound = ent->noise_index; // start it }else { // normal sound if ( ent->spawnflags & 8 ) { G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index ); } else if (ent->spawnflags & 4) { G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index ); } else { G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index ); } } } void SP_target_speaker( gentity_t *ent ) { char buffer[MAX_QPATH]; char *s; G_SpawnFloat( "wait", "0", &ent->wait ); G_SpawnFloat( "random", "0", &ent->random ); if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) { G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) ); } // force all client reletive sounds to be "activator" speakers that // play on the entity that activates it if ( s[0] == '*' ) { ent->spawnflags |= 8; } if (!strstr( s, ".wav" )) { Com_sprintf (buffer, sizeof(buffer), "%s.wav", s ); } else { Q_strncpyz( buffer, s, sizeof(buffer) ); } ent->noise_index = G_SoundIndex(buffer); // a repeating speaker can be done completely client side ent->s.eType = ET_SPEAKER; ent->s.eventParm = ent->noise_index; ent->s.frame = ent->wait * 10; ent->s.clientNum = ent->random * 10; // check for prestarted looping sound if ( ent->spawnflags & 1 ) { ent->s.loopSound = ent->noise_index; } ent->use = Use_Target_Speaker; if (ent->spawnflags & 4) { ent->r.svFlags |= SVF_BROADCAST; } VectorCopy( ent->s.origin, ent->s.pos.trBase ); // must link the entity so we get areas and clusters so // the server can determine who to send updates to trap_LinkEntity( ent ); } //========================================================== /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON When triggered, fires a laser. You can either set a target or a direction. */ void target_laser_think (gentity_t *self) { vec3_t end; trace_t tr; vec3_t point; // if pointed at another entity, set movedir to point at it if ( self->enemy ) { VectorMA (self->enemy->s.origin, 0.5, self->enemy->r.mins, point); VectorMA (point, 0.5, self->enemy->r.maxs, point); VectorSubtract (point, self->s.origin, self->movedir); VectorNormalize (self->movedir); } // fire forward and see what we hit VectorMA (self->s.origin, 2048, self->movedir, end); trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE); if ( tr.entityNum ) { // hurt it if we can G_Damage ( &g_entities[tr.entityNum], self, self->activator, self->movedir, tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER); } VectorCopy (tr.endpos, self->s.origin2); trap_LinkEntity( self ); self->nextthink = level.time + FRAMETIME; } void target_laser_on (gentity_t *self) { if (!self->activator) self->activator = self; target_laser_think (self); } void target_laser_off (gentity_t *self) { trap_UnlinkEntity( self ); self->nextthink = 0; } void target_laser_use (gentity_t *self, gentity_t *other, gentity_t *activator) { self->activator = activator; if ( self->nextthink > 0 ) target_laser_off (self); else target_laser_on (self); } void target_laser_start (gentity_t *self) { gentity_t *ent; self->s.eType = ET_BEAM; if (self->target) { ent = G_Find (NULL, FOFS(targetname), self->target); if (!ent) { G_Printf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target); } self->enemy = ent; } else { G_SetMovedir (self->s.angles, self->movedir); } self->use = target_laser_use; self->think = target_laser_think; if ( !self->damage ) { self->damage = 1; } if (self->spawnflags & 1) target_laser_on (self); else target_laser_off (self); } void SP_target_laser (gentity_t *self) { // let everything else get spawned before we start firing self->think = target_laser_start; self->nextthink = level.time + FRAMETIME; } //========================================================== void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { gentity_t *dest; if (!activator->client) return; dest = G_PickTarget( self->target ); if (!dest) { G_Printf ("Couldn't find teleporter destination\n"); return; } TeleportPlayer( activator, dest->s.origin, dest->s.angles ); } /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8) The activator will be teleported away. */ void SP_target_teleporter( gentity_t *self ) { if (!self->targetname) G_Printf("untargeted %s at %s\n", self->classname, vtos(self->s.origin)); self->use = target_teleporter_use; } //========================================================== /*QUAKED target_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM This doesn't perform any actions except fire its targets. The activator can be forced to be from a certain team. if RANDOM is checked, only one of the targets will be fired, not all of them */ void target_relay_use (gentity_t *self, gentity_t *other, gentity_t *activator) { if ( ( self->spawnflags & 1 ) && activator->client && activator->client->sess.sessionTeam != TEAM_RED ) { return; } if ( ( self->spawnflags & 2 ) && activator->client && activator->client->sess.sessionTeam != TEAM_BLUE ) { return; } if ( self->spawnflags & 4 ) { gentity_t *ent; ent = G_PickTarget( self->target ); if ( ent && ent->use ) { ent->use( ent, self, activator ); } return; } G_UseTargets (self, activator); } void SP_target_relay (gentity_t *self) { self->use = target_relay_use; } //========================================================== /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8) Kills the activator. */ void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } void SP_target_kill( gentity_t *self ) { self->use = target_kill_use; } /*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for in-game calculation, like jumppad targets. */ void SP_target_position( gentity_t *self ){ G_SetOrigin( self, self->s.origin ); } static void target_location_linkup(gentity_t *ent) { int i; int n; //static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "elimination", "ctf", "lms", "dd", "dom"}; if (level.locationLinked) return; level.locationLinked = qtrue; level.locationHead = NULL; trap_SetConfigstring( CS_LOCATIONS, "unknown" ); for (i = 0, ent = g_entities, n = 1; i < level.num_entities; i++, ent++) { if (ent->classname && !Q_stricmp(ent->classname, "target_location") ) { // lets overload some variables! ent->health = n; // use for location marking trap_SetConfigstring( CS_LOCATIONS + n, ent->message ); n++; ent->nextTrain = level.locationHead; level.locationHead = ent; } } // All linked together now } /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8) Set "message" to the name of this location. Set "count" to 0-7 for color. 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white Closest target_location in sight used for the location, if none in site, closest in distance */ void SP_target_location( gentity_t *self ){ self->think = target_location_linkup; self->nextthink = level.time + 200; // Let them all spawn first G_SetOrigin( self, self->s.origin ); } openarena_0.8.8.orig/code/game/g_cmds_ext.c0000644000175000017500000004133111656310264017333 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2004-2006 Tony J. White This file is part of the Open Arena source code. Copied from Tremulous under GPL version 2 including any later version. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" /* ================= G_SayArgc G_SayArgv G_SayConcatArgs trap_Argc, trap_Argv, and ConcatArgs consider say text as a single argument These functions assemble the text and re-parse it on word boundaries ================= */ int G_SayArgc( void ) { int c = 0; char *s; s = ConcatArgs( 0 ); while( 1 ) { while( *s == ' ' ) s++; if( !*s ) break; c++; while( *s && *s != ' ' ) s++; } return c; } qboolean G_SayArgv( int n, char *buffer, int bufferLength ) { char *s; if( bufferLength < 1 ) return qfalse; if( n < 0 ) return qfalse; s = ConcatArgs( 0 ); while( 1 ) { while( *s == ' ' ) s++; if( !*s || n == 0 ) break; n--; while( *s && *s != ' ' ) s++; } if( n > 0 ) return qfalse; //memccpy( buffer, s, ' ', bufferLength ); while( *s && *s != ' ' && bufferLength > 1 ) { *buffer++ = *s++; bufferLength--; } *buffer = 0; return qtrue; } char *G_SayConcatArgs( int start ) { char *s; s = ConcatArgs( 0 ); while( 1 ) { while( *s == ' ' ) s++; if( !*s || start == 0 ) break; start--; while( *s && *s != ' ' ) s++; } return s; } void G_DecolorString( char *in, char *out, int len ) { len--; while( *in && len > 0 ) { if( Q_IsColorString( in ) ) { in++; if( *in ) in++; continue; } *out++ = *in++; len--; } *out = '\0'; } /* ================== G_MatchOnePlayer This is a companion function to G_ClientNumbersFromString() err will be populated with an error message. ================== */ void G_MatchOnePlayer( int *plist, int num, char *err, int len ) { gclient_t *cl; int i; char line[ MAX_NAME_LENGTH + 10 ] = {""}; err[ 0 ] = '\0'; if( num == 0 ) { Q_strcat( err, len, "no connected player by that name or slot #" ); } else if( num > 1 ) { Q_strcat( err, len, "more than one player name matches. Be more specific or use the slot #:\n" ); for( i = 0; i < num; i++ ) { cl = &level.clients[ plist[ i ] ]; if( cl->pers.connected == CON_DISCONNECTED ) continue; Com_sprintf( line, sizeof( line ), "%2i - %s^7\n", plist[ i ], cl->pers.netname ); if( strlen( err ) + strlen( line ) > len ) break; Q_strcat( err, len, line ); } } } /* ================== G_SanitiseString Remove case and control characters from a string ================== */ void G_SanitiseString( char *in, char *out, int len ) { qboolean skip = qtrue; int spaces = 0; len--; while( *in && len > 0 ) { // strip leading white space if( *in == ' ' ) { if( skip ) { in++; continue; } spaces++; } else { spaces = 0; skip = qfalse; } if( Q_IsColorString( in ) ) { in += 2; // skip color code continue; } if( *in < 32 ) { in++; continue; } *out++ = tolower( *in++ ); len--; } out -= spaces; *out = 0; } /* ================== G_ClientNumbersFromString Sets plist to an array of integers that represent client numbers that have names that are a partial match for s. Returns number of matching clientids up to max. ================== */ int G_ClientNumbersFromString( char *s, int *plist, int max ) { gclient_t *p; int i, found = 0; char n2[ MAX_NAME_LENGTH ] = {""}; char s2[ MAX_NAME_LENGTH ] = {""}; if( max == 0 ) return 0; // if a number is provided, it is a clientnum for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ ); if( !s[ i ] ) { i = atoi( s ); if( i >= 0 && i < level.maxclients ) { p = &level.clients[ i ]; if( p->pers.connected != CON_DISCONNECTED ) { *plist = i; return 1; } } // we must assume that if only a number is provided, it is a clientNum return 0; } // now look for name matches G_SanitiseString( s, s2, sizeof( s2 ) ); if( strlen( s2 ) < 1 ) return 0; for( i = 0; i < level.maxclients && found < max; i++ ) { p = &level.clients[ i ]; if( p->pers.connected == CON_DISCONNECTED ) { continue; } G_SanitiseString( p->pers.netname, n2, sizeof( n2 ) ); if( strstr( n2, s2 ) ) { *plist++ = i; found++; } } return found; } /* ================== G_FloodLimited Determine whether a user is flood limited, and adjust their flood demerits Print them a warning message if they are over the limit Return is time in msec until the user can speak again ================== */ int G_FloodLimited( gentity_t *ent ) { int deltatime, ms; if( g_floodMinTime.integer <= 0 ) return 0; // handles !ent if( G_admin_permission( ent, ADMF_NOCENSORFLOOD ) ) return 0; deltatime = level.time - ent->client->pers.floodTime; ent->client->pers.floodDemerits += g_floodMinTime.integer - deltatime; if( ent->client->pers.floodDemerits < 0 ) ent->client->pers.floodDemerits = 0; ent->client->pers.floodTime = level.time; ms = ent->client->pers.floodDemerits - g_floodMaxDemerits.integer; if( ms <= 0 ) return 0; trap_SendServerCommand( ent - g_entities, va( "print \"You are flooding: please wait %d second%s before trying again\n", ( ms + 999 ) / 1000, ( ms > 1000 ) ? "s" : "" ) ); return ms; } //KK-Private Messaging Not implemented. /* void Cmd_PrivateMessage_f( gentity_t *ent ) { int pids[ MAX_CLIENTS ]; int ignoreids[ MAX_CLIENTS ]; char name[ MAX_NAME_LENGTH ]; char cmd[ 12 ]; char str[ MAX_STRING_CHARS ]; char *msg; char color; int pcount, matches, ignored = 0; int i; int skipargs = 0; qboolean teamonly = qfalse; gentity_t *tmpent; if( !g_privateMessages.integer && ent ) { ADMP( "Sorry, but private messages have been disabled\n" ); return; } G_SayArgv( 0, cmd, sizeof( cmd ) ); if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) ) { skipargs = 1; G_SayArgv( 1, cmd, sizeof( cmd ) ); } if( G_SayArgc( ) < 3+skipargs ) { ADMP( va( "usage: %s [name|slot#] [message]\n", cmd ) ); return; } if( !Q_stricmp( cmd, "mt" ) || !Q_stricmp( cmd, "/mt" ) ) teamonly = qtrue; G_SayArgv( 1+skipargs, name, sizeof( name ) ); msg = G_SayConcatArgs( 2+skipargs ); pcount = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ); if( ent ) { int count = 0; for( i = 0; i < pcount; i++ ) { tmpent = &g_entities[ pids[ i ] ]; if( teamonly && !OnSameTeam( ent, tmpent ) ) continue; if( BG_ClientListTest( &tmpent->client->pers.ignoreList, ent-g_entities ) ) { ignoreids[ ignored++ ] = pids[ i ]; continue; } pids[ count ] = pids[ i ]; count++; } matches = count; } else { matches = pcount; } color = teamonly ? COLOR_CYAN : COLOR_YELLOW; Com_sprintf( str, sizeof( str ), "^%csent to %i player%s: ^7", color, matches, ( matches == 1 ) ? "" : "s" ); for( i=0; i < matches; i++ ) { tmpent = &g_entities[ pids[ i ] ]; if( i > 0 ) Q_strcat( str, sizeof( str ), "^7, " ); Q_strcat( str, sizeof( str ), tmpent->client->pers.netname ); trap_SendServerCommand( pids[ i ], va( "chat \"%s^%c -> ^7%s^7: (%d recipient%s): ^%c%s^7\" %i", ( ent ) ? ent->client->pers.netname : "console", color, name, matches, ( matches == 1 ) ? "" : "s", color, msg, ent ? ent-g_entities : -1 ) ); if( ent ) { trap_SendServerCommand( pids[ i ], va( "print \">> to reply, say: /m %d [your message] <<\n\"", ( ent - g_entities ) ) ); } trap_SendServerCommand( pids[ i ], va( "cp \"^%cprivate message from ^7%s^7\"", color, ( ent ) ? ent->client->pers.netname : "console" ) ); } if( !matches ) ADMP( va( "^3No player matching ^7\'%s^7\' ^3to send message to.\n", name ) ); else { ADMP( va( "^%cPrivate message: ^7%s\n", color, msg ) ); ADMP( va( "%s\n", str ) ); G_LogPrintf( "%s: %s^7: %s^7: %s\n", ( teamonly ) ? "tprivmsg" : "privmsg", ( ent ) ? ent->client->pers.netname : "console", name, msg ); } if( ignored ) { Com_sprintf( str, sizeof( str ), "^%cignored by %i player%s: ^7", color, ignored, ( ignored == 1 ) ? "" : "s" ); for( i=0; i < ignored; i++ ) { tmpent = &g_entities[ ignoreids[ i ] ]; if( i > 0 ) Q_strcat( str, sizeof( str ), "^7, " ); Q_strcat( str, sizeof( str ), tmpent->client->pers.netname ); } ADMP( va( "%s\n", str ) ); } } */ /* ================= G_AdminMessage Print to all active server admins, and to the logfile, and to the server console Prepend *prefix, or '[SERVER]' if no *prefix is given ================= */ void QDECL G_AdminMessage( const char *prefix, const char *fmt, ... ) { va_list argptr; char string[ 1024 ]; char outstring[ 1024 ]; int i; // Format the text va_start( argptr, fmt ); Q_vsnprintf( string, sizeof( string ), fmt, argptr ); va_end( argptr ); // If there is no prefix, assume that this function was called directly // and we should add one if( !prefix || !prefix[ 0 ] ) { prefix = "[SERVER]:"; } // Create the final string Com_sprintf( outstring, sizeof( outstring ), "%s " S_COLOR_MAGENTA "%s", prefix, string ); // Send to all appropriate clients for( i = 0; i < level.maxclients; i++ ) if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) ) trap_SendServerCommand( i, va( "chat \"%s\"", outstring ) ); // Send to the logfile and server console G_LogPrintf("adminmsg: %s\n", outstring ); } /* ================= Cmd_AdminMessage_f Send a message to all active admins ================= */ void Cmd_AdminMessage_f( gentity_t *ent ) { char cmd[ sizeof( "say_team" ) ]; char prefix[ 50 ]; char *msg; int skiparg = 0; // Check permissions and add the appropriate user [prefix] if( !ent ) { Com_sprintf( prefix, sizeof( prefix ), "[CONSOLE]:" ); } else if( !G_admin_permission( ent, ADMF_ADMINCHAT ) ) { if( !g_publicAdminMessages.integer ) { ADMP( "Sorry, but use of /a by non-admins has been disabled.\n" ); return; } else { Com_sprintf( prefix, sizeof( prefix ), "[PLAYER] %s" S_COLOR_WHITE ":", ent->client->pers.netname ); ADMP( "Your message has been sent to any available admins and to the server logs.\n" ); } } else { Com_sprintf( prefix, sizeof( prefix ), "[ADMIN] %s" S_COLOR_WHITE ":", ent->client->pers.netname ); } // Skip say/say_team if this was used from one of those G_SayArgv( 0, cmd, sizeof( cmd ) ); if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) ) { skiparg = 1; G_SayArgv( 1, cmd, sizeof( cmd ) ); } if( G_SayArgc( ) < 2 + skiparg ) { ADMP( va( "usage: %s [message]\n", cmd ) ); return; } msg = G_SayConcatArgs( 1 + skiparg ); // Send it G_AdminMessage( prefix, "%s", msg ); } /* ============== Cmd_Ignore_f KK-OAX Removed Static to Keep in Mod Files Currently Unused until I figure out how to implement it with voice chats. ============== */ /*void Cmd_Ignore_f( gentity_t *ent ) { int pids[ MAX_CLIENTS ]; char name[ MAX_NAME_LENGTH ]; char cmd[ 9 ]; int matches = 0; int i; qboolean ignore = qfalse; trap_Argv( 0, cmd, sizeof( cmd ) ); if( Q_stricmp( cmd, "ignore" ) == 0 ) ignore = qtrue; if( trap_Argc() < 2 ) { trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "usage: %s [clientNum | partial name match]\n\"", cmd ) ); return; } Q_strncpyz( name, ConcatArgs( 1 ), sizeof( name ) ); matches = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ); if( matches < 1 ) { trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "%s: no clients match the name '%s'\n\"", cmd, name ) ); return; } for( i = 0; i < matches; i++ ) { if( ignore ) { if( !BG_ClientListTest( &ent->client->pers.ignoreList, pids[ i ] ) ) { BG_ClientListAdd( &ent->client->pers.ignoreList, pids[ i ] ); ClientUserinfoChanged( ent->client->ps.clientNum ); trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "ignore: added %s^7 to your ignore list\n\"", level.clients[ pids[ i ] ].pers.netname ) ); } else { trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "ignore: %s^7 is already on your ignore list\n\"", level.clients[ pids[ i ] ].pers.netname ) ); } } else { if( BG_ClientListTest( &ent->client->pers.ignoreList, pids[ i ] ) ) { BG_ClientListRemove( &ent->client->pers.ignoreList, pids[ i ] ); ClientUserinfoChanged( ent->client->ps.clientNum ); trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "unignore: removed %s^7 from your ignore list\n\"", level.clients[ pids[ i ] ].pers.netname ) ); } else { trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" "unignore: %s^7 is not on your ignore list\n\"", level.clients[ pids[ i ] ].pers.netname ) ); } } } } */ /* ================== G_ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int G_ClientNumberFromString( char *s ) { gclient_t *cl; int i; char s2[ MAX_NAME_LENGTH ]; char n2[ MAX_NAME_LENGTH ]; // numeric values are just slot numbers for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ ); if( !s[ i ] ) { i = atoi( s ); if( i < 0 || i >= level.maxclients ) return -1; cl = &level.clients[ i ]; if( cl->pers.connected == CON_DISCONNECTED ) return -1; return i; } // check for a name match G_SanitiseString( s, s2, sizeof( s2 ) ); for( i = 0, cl = level.clients; i < level.maxclients; i++, cl++ ) { if( cl->pers.connected == CON_DISCONNECTED ) continue; G_SanitiseString( cl->pers.netname, n2, sizeof( n2 ) ); if( !strcmp( n2, s2 ) ) return i; } return -1; } //KK-OAX Used to !spec999'ers /* ============= G_ClientIsLagging ============= */ qboolean G_ClientIsLagging( gclient_t *client ) { if( client ) { if( client->ps.ping >= 999 ) return qtrue; else return qfalse; } return qfalse; //is a non-existant client lagging? woooo zen } /* ================== SanitizeString Remove case and control characters ================== */ void SanitizeString( char *in, char *out ) { while ( *in ) { if ( *in == 27 ) { in += 2; // skip color code continue; } if ( *in < 32 ) { in++; continue; } *out++ = tolower( *in++ ); } *out = 0; } openarena_0.8.8.orig/code/game/g_vote.c0000644000175000017500000003177311656310264016513 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2008-2009 Poul Sander This file is part of Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" /* ================== allowedVote *Note: Keep this in sync with allowedVote in ui_votemenu.c (except for cg_voteNames and g_voteNames) ================== */ #define MAX_VOTENAME_LENGTH 14 //currently the longest string is "/map_restart/\0" (14 chars) int allowedVote(char *commandStr) { char tempStr[MAX_VOTENAME_LENGTH]; int length; char voteNames[MAX_CVAR_VALUE_STRING]; trap_Cvar_VariableStringBuffer( "g_voteNames", voteNames, sizeof( voteNames ) ); if(!Q_stricmp(voteNames, "*" )) return qtrue; //if star, everything is allowed length = strlen(commandStr); if(length>MAX_VOTENAME_LENGTH-3) { //Error: too long return qfalse; } //Now constructing a string that starts and ends with '/' like: "/clientkick/" tempStr[0] = '/'; strncpy(&tempStr[1],commandStr,length); tempStr[length+1] = '/'; tempStr[length+2] = '\0'; if(Q_stristr(voteNames,tempStr) != NULL) return qtrue; else return qfalse; } /* ================== getMappage ================== */ t_mappage getMappage(int page) { t_mappage result; fileHandle_t file; char *token,*pointer; char buffer[MAX_MAPNAME_BUFFER]; int i, nummaps,maplen; memset(&result,0,sizeof(result)); memset(&buffer,0,sizeof(buffer)); //Check if there is a votemaps.cfg trap_FS_FOpenFile(g_votemaps.string,&file,FS_READ); if(file) { //there is a votemaps.cfg file, take allowed maps from there trap_FS_Read(&buffer,sizeof(buffer),file); pointer = buffer; token = COM_Parse(&pointer); if(token[0]==0 && page == 0) { //First page empty result.pagenumber = -1; trap_FS_FCloseFile(file); return result; } //Skip the first pages for(i=0;i=MAPS_PER_PAGE*page && iMAX_MAPNAME_LENGTH-3) { //Error: too long trap_FS_FCloseFile(file); return qfalse; } //Add file checking here trap_FS_Read(&buffer,MAX_MAPS_TEXT,file); found = qfalse; pointer = buffer; token = COM_Parse(&pointer); while(token[0]!=0 && !found) { if(!Q_stricmp(token,mapname)) found = qtrue; token = COM_Parse(&pointer); } trap_FS_FCloseFile(file); //The map was not found return found; } /* ================== allowedGametype ================== */ #define MAX_GAMETYPENAME_LENGTH 5 //currently the longest string is "/12/\0" (5 chars) int allowedGametype(char *gametypeStr) { char tempStr[MAX_GAMETYPENAME_LENGTH]; int length; char voteGametypes[MAX_CVAR_VALUE_STRING]; trap_Cvar_VariableStringBuffer( "g_voteGametypes", voteGametypes, sizeof( voteGametypes ) ); if(!Q_stricmp(voteGametypes, "*" )) return qtrue; //if star, everything is allowed length = strlen(gametypeStr); if(length>MAX_GAMETYPENAME_LENGTH-3) { //Error: too long return qfalse; } tempStr[0] = '/'; strncpy(&tempStr[1],gametypeStr,length); tempStr[length+1] = '/'; tempStr[length+2] = '\0'; if(Q_stristr(voteGametypes,tempStr) != NULL) return qtrue; else { return qfalse; } } /* ================== allowedTimelimit ================== */ int allowedTimelimit(int limit) { int min, max; min = g_voteMinTimelimit.integer; max = g_voteMaxTimelimit.integer; if(limitmax) return qfalse; if(limit==0 && max > 0) return qfalse; return qtrue; } /* ================== allowedFraglimit ================== */ int allowedFraglimit(int limit) { int min, max; min = g_voteMinFraglimit.integer; max = g_voteMaxFraglimit.integer; if(limitmax) return qfalse; if(limit==0 && max > 0) return qfalse; return qtrue; } #define MAX_CUSTOM_VOTES 12 char custom_vote_info[1024]; /* ================== VoteParseCustomVotes *Reads the file votecustom.cfg. Finds all the commands that can be used with *"/callvote custom COMMAND" and adds the commands to custom_vote_info ================== */ int VoteParseCustomVotes( void ) { fileHandle_t file; char buffer[4*1024]; int commands; char *token,*pointer; trap_FS_FOpenFile(g_votecustom.string,&file,FS_READ); if(!file) return 0; memset(&buffer,0,sizeof(buffer)); memset(&custom_vote_info,0,sizeof(custom_vote_info)); commands = 0; trap_FS_Read(&buffer,sizeof(buffer),file); pointer = buffer; while ( commands < MAX_CUSTOM_VOTES ) { token = COM_Parse( &pointer ); if ( !token[0] ) { break; } if ( !strcmp( token, "votecommand" ) ) { token = COM_Parse( &pointer ); Q_strcat(custom_vote_info,sizeof(custom_vote_info),va("%s ",token)); commands++; } } trap_FS_FCloseFile(file); return commands; } /* ================== getCustomVote *Returns a custom vote. This will go beyond MAX_CUSTOM_VOTES. ================== */ t_customvote getCustomVote(char* votecommand) { t_customvote result; fileHandle_t file; char buffer[4*1024]; char *token,*pointer; char key[MAX_TOKEN_CHARS]; trap_FS_FOpenFile(g_votecustom.string,&file,FS_READ); if(!file) { memset(&result,0,sizeof(result)); return result; } memset(&buffer,0,sizeof(buffer)); trap_FS_Read(&buffer,sizeof(buffer),file); pointer = buffer; while ( qtrue ) { token = COM_Parse( &pointer ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in votecustom.cfg\n" ); break; } memset(&result,0,sizeof(result)); while ( 1 ) { token = COM_ParseExt( &pointer, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of customvote.cfg\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &pointer, qfalse ); if ( !token[0] ) { Com_Printf("Invalid/missing argument to %s in customvote.cfg\n",key); } if(!Q_stricmp(key,"votecommand")) { Q_strncpyz(result.votename,token,sizeof(result.votename)); } else if(!Q_stricmp(key,"displayname")) { Q_strncpyz(result.displayname,token,sizeof(result.displayname)); } else if(!Q_stricmp(key,"command")) { Q_strncpyz(result.command,token,sizeof(result.command)); } else { Com_Printf("Unknown key in customvote.cfg: %s\n",key); } } if(!Q_stricmp(result.votename,votecommand)) { return result; } } //Nothing was found memset(&result,0,sizeof(result)); return result; } /* ================== CheckVote ================== */ void CheckVote( void ) { if ( level.voteExecuteTime && level.voteExecuteTime < level.time ) { level.voteExecuteTime = 0; trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) ); } if ( !level.voteTime ) { return; } if ( level.time - level.voteTime >= VOTE_TIME ) { if(g_dmflags.integer & DF_LIGHT_VOTING) { //Let pass if there was at least twice as many for as against if ( level.voteYes > level.voteNo*2 ) { trap_SendServerCommand( -1, "print \"Vote passed. At least 2 of 3 voted yes\n\"" ); level.voteExecuteTime = level.time + 3000; } else { //Let pass if there is more yes than no and at least 2 yes votes and at least 30% yes of all on the server if ( level.voteYes > level.voteNo && level.voteYes >= 2 && (level.voteYes*10)>(level.numVotingClients*3) ) { trap_SendServerCommand( -1, "print \"Vote passed. More yes than no.\n\"" ); level.voteExecuteTime = level.time + 3000; } else trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } } else { trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } } else { // ATVI Q3 1.32 Patch #9, WNF if ( level.voteYes > (level.numVotingClients)/2 ) { // execute the command, then remove the vote trap_SendServerCommand( -1, "print \"Vote passed.\n\"" ); level.voteExecuteTime = level.time + 3000; } else if ( level.voteNo >= (level.numVotingClients)/2 ) { // same behavior as a timeout trap_SendServerCommand( -1, "print \"Vote failed.\n\"" ); } else { // still waiting for a majority return; } } level.voteTime = 0; trap_SetConfigstring( CS_VOTE_TIME, "" ); } void ForceFail( void ) { level.voteTime = 0; level.voteExecuteTime = 0; level.voteString[0] = 0; level.voteDisplayString[0] = 0; level.voteKickClient = -1; level.voteKickType = 0; trap_SetConfigstring( CS_VOTE_TIME, "" ); trap_SetConfigstring( CS_VOTE_STRING, "" ); trap_SetConfigstring( CS_VOTE_YES, "" ); trap_SetConfigstring( CS_VOTE_NO, "" ); } /* ================== CountVotes Iterates through all the clients and counts the votes ================== */ void CountVotes( void ) { int i; int yes=0,no=0; level.numVotingClients=0; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[ i ].pers.connected != CON_CONNECTED ) continue; //Client was not connected if (level.clients[i].sess.sessionTeam == TEAM_SPECTATOR) continue; //Don't count spectators if ( g_entities[i].r.svFlags & SVF_BOT ) continue; //Is a bot //The client can vote level.numVotingClients++; //Did the client vote yes? if(level.clients[i].vote>0) yes++; //Did the client vote no? if(level.clients[i].vote<0) no++; } //See if anything has changed if(level.voteYes != yes) { level.voteYes = yes; trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) ); } if(level.voteNo != no) { level.voteNo = no; trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) ); } } void ClientLeaving(int clientNumber) { if(clientNumber == level.voteKickClient) { ForceFail(); } } openarena_0.8.8.orig/code/game/bg_misc.c0000644000175000017500000011566011656310264016631 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Open Arena source code. Portions copied from Tremulous under GPL version 2 including any later version. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_misc.c -- both games misc functions, all completely stateless #include "../qcommon/q_shared.h" #include "bg_public.h" /*QUAKED item_***** ( 0 0 0 ) (-16 -16 -16) (16 16 16) suspended DO NOT USE THIS CLASS, IT JUST HOLDS GENERAL INFORMATION. The suspended flag will allow items to hang in the air, otherwise they are dropped to the next surface. If an item is the target of another entity, it will not spawn in until fired. An item fires all of its targets when it is picked up. If the toucher can't carry it, the targets won't be fired. "notfree" if set to 1, don't spawn in free for all games "notteam" if set to 1, don't spawn in team games "notsingle" if set to 1, don't spawn in single player games "wait" override the default wait before respawning. -1 = never respawn automatically, which can be used with targeted spawning. "random" random number of plus or minus seconds varied from the respawn time "count" override quantity or duration on most items. */ gitem_t bg_itemlist[] = { { NULL, NULL, { NULL, NULL, NULL, NULL} , /* icon */ NULL, /* pickup */ NULL, 0, 0, 0, /* precache */ "", /* sounds */ "" }, // leave index 0 alone // // ARMOR // /*QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_shard", "sound/misc/ar1_pkup.wav", { "models/powerups/armor/shard.md3", "models/powerups/armor/shard_sphere.md3", NULL, NULL} , /* icon */ "icons/iconr_shard", /* pickup */ "Armor Shard", 5, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_combat", "sound/misc/ar2_pkup.wav", { "models/powerups/armor/armor_yel.md3", NULL, NULL, NULL}, /* icon */ "icons/iconr_yellow", /* pickup */ "Armor", 50, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_body", "sound/misc/ar2_pkup.wav", { "models/powerups/armor/armor_red.md3", NULL, NULL, NULL}, /* icon */ "icons/iconr_red", /* pickup */ "Heavy Armor", 100, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, // // health // /*QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_small", "sound/items/s_health.wav", { "models/powerups/health/small_cross.md3", "models/powerups/health/small_sphere.md3", NULL, NULL }, /* icon */ "icons/iconh_green", /* pickup */ "5 Health", 5, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health", "sound/items/n_health.wav", { "models/powerups/health/medium_cross.md3", "models/powerups/health/medium_sphere.md3", NULL, NULL }, /* icon */ "icons/iconh_yellow", /* pickup */ "25 Health", 25, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_large", "sound/items/l_health.wav", { "models/powerups/health/large_cross.md3", "models/powerups/health/large_sphere.md3", NULL, NULL }, /* icon */ "icons/iconh_red", /* pickup */ "50 Health", 50, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_mega", "sound/items/m_health.wav", { "models/powerups/health/mega_cross.md3", "models/powerups/health/mega_sphere.md3", NULL, NULL }, /* icon */ "icons/iconh_mega", /* pickup */ "Mega Health", 100, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, // // WEAPONS // /*QUAKED weapon_gauntlet (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_gauntlet", "sound/misc/w_pkup.wav", { "models/weapons2/gauntlet/gauntlet.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_gauntlet", /* pickup */ "Gauntlet", 0, IT_WEAPON, WP_GAUNTLET, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_shotgun", "sound/misc/w_pkup.wav", { "models/weapons2/shotgun/shotgun.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_shotgun", /* pickup */ "Shotgun", 10, IT_WEAPON, WP_SHOTGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_machinegun", "sound/misc/w_pkup.wav", { "models/weapons2/machinegun/machinegun.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_machinegun", /* pickup */ "Machinegun", 40, IT_WEAPON, WP_MACHINEGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_grenadelauncher", "sound/misc/w_pkup.wav", { "models/weapons2/grenadel/grenadel.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_grenade", /* pickup */ "Grenade Launcher", 10, IT_WEAPON, WP_GRENADE_LAUNCHER, /* precache */ "", /* sounds */ "sound/weapons/grenade/hgrenb1a.wav sound/weapons/grenade/hgrenb2a.wav" }, /*QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_rocketlauncher", "sound/misc/w_pkup.wav", { "models/weapons2/rocketl/rocketl.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_rocket", /* pickup */ "Rocket Launcher", 10, IT_WEAPON, WP_ROCKET_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_lightning", "sound/misc/w_pkup.wav", { "models/weapons2/lightning/lightning.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_lightning", /* pickup */ "Lightning Gun", 100, IT_WEAPON, WP_LIGHTNING, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_railgun", "sound/misc/w_pkup.wav", { "models/weapons2/railgun/railgun.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_railgun", /* pickup */ "Railgun", 10, IT_WEAPON, WP_RAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_plasmagun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_plasmagun", "sound/misc/w_pkup.wav", { "models/weapons2/plasma/plasma.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_plasma", /* pickup */ "Plasma Gun", 50, IT_WEAPON, WP_PLASMAGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_bfg", "sound/misc/w_pkup.wav", { "models/weapons2/bfg/bfg.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_bfg", /* pickup */ "BFG10K", 20, IT_WEAPON, WP_BFG, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_grapplinghook (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_grapplinghook", "sound/misc/w_pkup.wav", { "models/weapons2/grapple/grapple.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_grapple", /* pickup */ "Grappling Hook", 0, IT_WEAPON, WP_GRAPPLING_HOOK, /* precache */ "", /* sounds */ "" }, // // AMMO ITEMS // /*QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_shells", "sound/misc/am_pkup.wav", { "models/powerups/ammo/shotgunam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_shotgun", /* pickup */ "Shells", 10, IT_AMMO, WP_SHOTGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_bullets", "sound/misc/am_pkup.wav", { "models/powerups/ammo/machinegunam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_machinegun", /* pickup */ "Bullets", 50, IT_AMMO, WP_MACHINEGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_grenades", "sound/misc/am_pkup.wav", { "models/powerups/ammo/grenadeam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_grenade", /* pickup */ "Grenades", 5, IT_AMMO, WP_GRENADE_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_cells", "sound/misc/am_pkup.wav", { "models/powerups/ammo/plasmaam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_plasma", /* pickup */ "Cells", 30, IT_AMMO, WP_PLASMAGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_lightning", "sound/misc/am_pkup.wav", { "models/powerups/ammo/lightningam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_lightning", /* pickup */ "Lightning", 60, IT_AMMO, WP_LIGHTNING, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_rockets", "sound/misc/am_pkup.wav", { "models/powerups/ammo/rocketam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_rocket", /* pickup */ "Rockets", 5, IT_AMMO, WP_ROCKET_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_slugs", "sound/misc/am_pkup.wav", { "models/powerups/ammo/railgunam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_railgun", /* pickup */ "Slugs", 10, IT_AMMO, WP_RAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_bfg", "sound/misc/am_pkup.wav", { "models/powerups/ammo/bfgam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_bfg", /* pickup */ "Bfg Ammo", 15, IT_AMMO, WP_BFG, /* precache */ "", /* sounds */ "" }, // // HOLDABLE ITEMS // /*QUAKED holdable_teleporter (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_teleporter", "sound/items/holdable.wav", { "models/powerups/holdable/teleporter.md3", NULL, NULL, NULL}, /* icon */ "icons/teleporter", /* pickup */ "Personal Teleporter", 60, IT_HOLDABLE, HI_TELEPORTER, /* precache */ "", /* sounds */ "" }, /*QUAKED holdable_medkit (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_medkit", "sound/items/holdable.wav", { "models/powerups/holdable/medkit.md3", "models/powerups/holdable/medkit_sphere.md3", NULL, NULL}, /* icon */ "icons/medkit", /* pickup */ "Medkit", 60, IT_HOLDABLE, HI_MEDKIT, /* precache */ "", /* sounds */ "sound/items/use_medkit.wav" }, // // POWERUP ITEMS // /*QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_quad", "sound/items/quaddamage.wav", { "models/powerups/instant/quad.md3", "models/powerups/instant/quad_ring.md3", NULL, NULL }, /* icon */ "icons/quad", /* pickup */ "Quad Damage", 30, IT_POWERUP, PW_QUAD, /* precache */ "", /* sounds */ "sound/items/damage2.wav sound/items/damage3.wav" }, /*QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_enviro", "sound/items/protect.wav", { "models/powerups/instant/enviro.md3", "models/powerups/instant/enviro_ring.md3", NULL, NULL }, /* icon */ "icons/envirosuit", /* pickup */ "Battle Suit", 30, IT_POWERUP, PW_BATTLESUIT, /* precache */ "", /* sounds */ "sound/items/airout.wav sound/items/protect3.wav" }, /*QUAKED item_haste (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_haste", "sound/items/haste.wav", { "models/powerups/instant/haste.md3", "models/powerups/instant/haste_ring.md3", NULL, NULL }, /* icon */ "icons/haste", /* pickup */ "Speed", 30, IT_POWERUP, PW_HASTE, /* precache */ "", /* sounds */ "" }, /*QUAKED item_invis (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_invis", "sound/items/invisibility.wav", { "models/powerups/instant/invis.md3", "models/powerups/instant/invis_ring.md3", NULL, NULL }, /* icon */ "icons/invis", /* pickup */ "Invisibility", 30, IT_POWERUP, PW_INVIS, /* precache */ "", /* sounds */ "" }, /*QUAKED item_regen (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_regen", "sound/items/regeneration.wav", { "models/powerups/instant/regen.md3", "models/powerups/instant/regen_ring.md3", NULL, NULL }, /* icon */ "icons/regen", /* pickup */ "Regeneration", 30, IT_POWERUP, PW_REGEN, /* precache */ "", /* sounds */ "sound/items/regen.wav" }, /*QUAKED item_flight (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_flight", "sound/items/flight.wav", { "models/powerups/instant/flight.md3", "models/powerups/instant/flight_ring.md3", NULL, NULL }, /* icon */ "icons/flight", /* pickup */ "Flight", 60, IT_POWERUP, PW_FLIGHT, /* precache */ "", /* sounds */ "sound/items/flight.wav" }, /*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_redflag", NULL, { "models/flags/r_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_red1", /* pickup */ "Red Flag", 0, IT_TEAM, PW_REDFLAG, /* precache */ "", /* sounds */ "" }, /*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_blueflag", NULL, { "models/flags/b_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_blu1", /* pickup */ "Blue Flag", 0, IT_TEAM, PW_BLUEFLAG, /* precache */ "", /* sounds */ "" }, /*QUAKED holdable_kamikaze (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_kamikaze", "sound/items/holdable.wav", { "models/powerups/kamikazi.md3", NULL, NULL, NULL}, /* icon */ "icons/kamikaze", /* pickup */ "Kamikaze", 60, IT_HOLDABLE, HI_KAMIKAZE, /* precache */ "", /* sounds */ "sound/items/kamikazerespawn.wav" }, /*QUAKED holdable_portal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_portal", "sound/items/holdable.wav", { "models/powerups/holdable/porter.md3", NULL, NULL, NULL}, /* icon */ "icons/portal", /* pickup */ "Portal", 60, IT_HOLDABLE, HI_PORTAL, /* precache */ "", /* sounds */ "" }, /*QUAKED holdable_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_invulnerability", "sound/items/holdable.wav", { "models/powerups/holdable/invulnerability.md3", NULL, NULL, NULL}, /* icon */ "icons/invulnerability", /* pickup */ "Invulnerability", 60, IT_HOLDABLE, HI_INVULNERABILITY, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_nails (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_nails", "sound/misc/am_pkup.wav", { "models/powerups/ammo/nailgunam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_nailgun", /* pickup */ "Nails", 20, IT_AMMO, WP_NAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_mines (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_mines", "sound/misc/am_pkup.wav", { "models/powerups/ammo/proxmineam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_proxlauncher", /* pickup */ "Proximity Mines", 10, IT_AMMO, WP_PROX_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_belt (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_belt", "sound/misc/am_pkup.wav", { "models/powerups/ammo/chaingunam.md3", NULL, NULL, NULL}, /* icon */ "icons/icona_chaingun", /* pickup */ "Chaingun Belt", 100, IT_AMMO, WP_CHAINGUN, /* precache */ "", /* sounds */ "" }, // // PERSISTANT POWERUP ITEMS // /*QUAKED item_scout (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam */ { "item_scout", "sound/items/scout.wav", { "models/powerups/scout.md3", NULL, NULL, NULL }, /* icon */ "icons/scout", /* pickup */ "Scout", 30, IT_PERSISTANT_POWERUP, PW_SCOUT, /* precache */ "", /* sounds */ "" }, /*QUAKED item_guard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam */ { "item_guard", "sound/items/guard.wav", { "models/powerups/guard.md3", NULL, NULL, NULL }, /* icon */ "icons/guard", /* pickup */ "Guard", 30, IT_PERSISTANT_POWERUP, PW_GUARD, /* precache */ "", /* sounds */ "" }, /*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam */ { "item_doubler", "sound/items/doubler.wav", { "models/powerups/doubler.md3", NULL, NULL, NULL }, /* icon */ "icons/doubler", /* pickup */ "Doubler", 30, IT_PERSISTANT_POWERUP, PW_DOUBLER, /* precache */ "", /* sounds */ "" }, /*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam */ { "item_ammoregen", "sound/items/ammoregen.wav", { "models/powerups/ammo.md3", NULL, NULL, NULL }, /* icon */ "icons/ammo_regen", /* pickup */ "Ammo Regen", 30, IT_PERSISTANT_POWERUP, PW_AMMOREGEN, /* precache */ "", /* sounds */ "" }, /*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16) Only in One Flag CTF games */ { "team_CTF_neutralflag", NULL, { "models/flags/n_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_neutral1", /* pickup */ "Neutral Flag", 0, IT_TEAM, PW_NEUTRALFLAG, /* precache */ "", /* sounds */ "" }, { "item_redcube", "sound/misc/am_pkup.wav", { "models/powerups/orb/r_orb.md3", NULL, NULL, NULL }, /* icon */ "icons/iconh_rorb", /* pickup */ "Red Cube", 0, IT_TEAM, 0, /* precache */ "", /* sounds */ "" }, { "item_bluecube", "sound/misc/am_pkup.wav", { "models/powerups/orb/b_orb.md3", NULL, NULL, NULL }, /* icon */ "icons/iconh_borb", /* pickup */ "Blue Cube", 0, IT_TEAM, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_nailgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_nailgun", "sound/misc/w_pkup.wav", { "models/weapons/nailgun/nailgun.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_nailgun", /* pickup */ "Nailgun", 10, IT_WEAPON, WP_NAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_prox_launcher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_prox_launcher", "sound/misc/w_pkup.wav", { "models/weapons/proxmine/proxmine.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_proxlauncher", /* pickup */ "Prox Launcher", 5, IT_WEAPON, WP_PROX_LAUNCHER, /* precache */ "", /* sounds */ "sound/weapons/proxmine/wstbtick.wav " "sound/weapons/proxmine/wstbactv.wav " "sound/weapons/proxmine/wstbimpl.wav " "sound/weapons/proxmine/wstbimpm.wav " "sound/weapons/proxmine/wstbimpd.wav " "sound/weapons/proxmine/wstbactv.wav" }, /*QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_chaingun", "sound/misc/w_pkup.wav", { "models/weapons/vulcan/vulcan.md3", NULL, NULL, NULL}, /* icon */ "icons/iconw_chaingun", /* pickup */ "Chaingun", 80, IT_WEAPON, WP_CHAINGUN, /* precache */ "", /* sounds */ "sound/weapons/vulcan/wvulwind.wav" }, //#endif /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointAblue", NULL, { "models/dpoints/a_blue.md3", NULL, NULL, NULL }, /* icon */ "icons/icona_blue", /* pickup */ "Point A (Blue)", 0, IT_TEAM, DD_POINTABLUE, /* precache */ "", /* sounds */ "" }, /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointBblue", NULL, { "models/dpoints/b_blue.md3", NULL, NULL, NULL }, /* icon */ "icons/iconb_blue", /* pickup */ "Point B (Blue)", 0, IT_TEAM, DD_POINTBBLUE, /* precache */ "", /* sounds */ "" }, /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointAred", NULL, { "models/dpoints/a_red.md3", NULL, NULL, NULL }, /* icon */ "icons/icona_red", /* pickup */ "Point A (Red)", 0, IT_TEAM, DD_POINTARED, /* precache */ "", /* sounds */ "" }, /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointBred", NULL, { "models/dpoints/b_red.md3", NULL, NULL, NULL }, /* icon */ "icons/iconb_red", /* pickup */ "Point B (Red)", 0, IT_TEAM, DD_POINTBRED, /* precache */ "", /* sounds */ "" }, /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointAwhite", NULL, { "models/dpoints/a_white.md3", NULL, NULL, NULL }, /* icon */ "icons/icona_white", /* pickup */ "Point A (White)", 0, IT_TEAM, DD_POINTAWHITE, /* precache */ "", /* sounds */ "" }, /*QUAKED team_DD_point Only in DD games */ { "team_DD_pointBwhite", NULL, { "models/dpoints/b_white.md3", NULL, NULL, NULL }, /* icon */ "icons/iconb_white", /* pickup */ "Point B (White)", 0, IT_TEAM, DD_POINTBWHITE, /* precache */ "", /* sounds */ "" }, //Now things for standard domination: /*QUAKED Only in Domination games */ { "team_dom_pointWhite", NULL, { "models/flags/n_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_neutral1", /* pickup */ "Neutral domination point", 0, IT_TEAM, DOM_POINTWHITE, /* precache */ "", /* sounds */ "" }, /*QUAKED Only in Domination games */ { "team_dom_pointRed", NULL, { "models/flags/r_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_red1", /* pickup */ "Red domination point", 0, IT_TEAM, DOM_POINTRED, /* precache */ "", /* sounds */ "" }, /*QUAKED Only in Domination games */ { "team_dom_pointBlue", NULL, { "models/flags/b_flag.md3", NULL, NULL, NULL }, /* icon */ "icons/iconf_blu1", /* pickup */ "Blue domination point", 0, IT_TEAM, DOM_POINTBLUE, /* precache */ "", /* sounds */ "" }, // end of list marker {NULL} }; int bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1; /* ============== BG_FindItemForPowerup ============== */ gitem_t *BG_FindItemForPowerup( powerup_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( (bg_itemlist[i].giType == IT_POWERUP || bg_itemlist[i].giType == IT_TEAM || bg_itemlist[i].giType == IT_PERSISTANT_POWERUP) && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } return NULL; } /* ============== BG_FindItemForHoldable ============== */ gitem_t *BG_FindItemForHoldable( holdable_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( bg_itemlist[i].giType == IT_HOLDABLE && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } Com_Error( ERR_DROP, "HoldableItem not found" ); return NULL; } /* =============== BG_FindItemForWeapon =============== */ gitem_t *BG_FindItemForWeapon( weapon_t weapon ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++) { if ( it->giType == IT_WEAPON && it->giTag == weapon ) { return it; } } Com_Error( ERR_DROP, "Couldn't find item for weapon %i", weapon); return NULL; } /* =============== BG_FindItem =============== */ gitem_t *BG_FindItem( const char *pickupName ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++ ) { if ( !Q_stricmp( it->pickup_name, pickupName ) ) return it; } return NULL; } /* ============ BG_PlayerTouchesItem Items can be picked up without actually touching their physical bounds to make grabbing them easier ============ */ qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) { vec3_t origin; BG_EvaluateTrajectory( &item->pos, atTime, origin ); // we are ignoring ducked differences here if ( ps->origin[0] - origin[0] > 44 || ps->origin[0] - origin[0] < -50 || ps->origin[1] - origin[1] > 36 || ps->origin[1] - origin[1] < -36 || ps->origin[2] - origin[2] > 36 || ps->origin[2] - origin[2] < -36 ) { return qfalse; } return qtrue; } /* ================ BG_CanItemBeGrabbed Returns false if the item should not be picked up. This needs to be the same for client side prediction and server use. ================ */ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ) { gitem_t *item; int upperBound; if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) { Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" ); } item = &bg_itemlist[ent->modelindex]; switch( item->giType ) { case IT_WEAPON: return qtrue; // weapons are always picked up case IT_AMMO: if ( ps->ammo[ item->giTag ] >= 200 ) { return qfalse; // can't hold any more } return qtrue; case IT_ARMOR: if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { return qfalse; } // we also clamp armor to the maxhealth for handicapping if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { upperBound = ps->stats[STAT_MAX_HEALTH]; } else { upperBound = ps->stats[STAT_MAX_HEALTH] * 2; } if ( ps->stats[STAT_ARMOR] >= upperBound ) { return qfalse; } return qtrue; case IT_HEALTH: // small and mega healths will go over the max, otherwise // don't pick up if already at max if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { upperBound = ps->stats[STAT_MAX_HEALTH]; } else if ( item->quantity == 5 || item->quantity == 100 ) { if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; } return qtrue; } if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] ) { return qfalse; } return qtrue; case IT_POWERUP: return qtrue; // powerups are always picked up case IT_PERSISTANT_POWERUP: //In Double D we don't want persistant Powerups (or maybe, can be discussed) if(gametype == GT_DOUBLE_D) return qfalse; // can only hold one item at a time if ( ps->stats[STAT_PERSISTANT_POWERUP] ) { return qfalse; } // check team only if( ( ent->generic1 & 2 ) && ( ps->persistant[PERS_TEAM] != TEAM_RED ) ) { return qfalse; } if( ( ent->generic1 & 4 ) && ( ps->persistant[PERS_TEAM] != TEAM_BLUE ) ) { return qfalse; } return qtrue; case IT_TEAM: // team items, such as flags if( gametype == GT_1FCTF ) { // neutral flag can always be picked up if( item->giTag == PW_NEUTRALFLAG ) { return qtrue; } if (ps->persistant[PERS_TEAM] == TEAM_RED) { if (item->giTag == PW_BLUEFLAG && ps->powerups[PW_NEUTRALFLAG] ) { return qtrue; } } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG && ps->powerups[PW_NEUTRALFLAG] ) { return qtrue; } } } if( gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { // ent->modelindex2 is non-zero on items if they are dropped // we need to know this because we can pick up our dropped flag (and return it) // but we can't pick up our flag at base if (ps->persistant[PERS_TEAM] == TEAM_RED) { if (item->giTag == PW_BLUEFLAG || (item->giTag == PW_REDFLAG && ent->modelindex2) || (item->giTag == PW_REDFLAG && ps->powerups[PW_BLUEFLAG]) ) return qtrue; } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG || (item->giTag == PW_BLUEFLAG && ent->modelindex2) || (item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) ) return qtrue; } } if( gametype == GT_DOUBLE_D) { //We can touch both flags if(item->giTag == PW_BLUEFLAG || item->giTag == PW_REDFLAG) return qtrue; } if( gametype == GT_DOMINATION ) { if(item->giTag == DOM_POINTWHITE) return qtrue; if (ps->persistant[PERS_TEAM] == TEAM_RED) { if(item->giTag == DOM_POINTBLUE) return qtrue; } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { if(item->giTag == DOM_POINTRED) return qtrue; } } if( gametype == GT_HARVESTER ) { return qtrue; } return qfalse; case IT_HOLDABLE: // can only hold one item at a time if ( ps->stats[STAT_HOLDABLE_ITEM] ) { return qfalse; } return qtrue; case IT_BAD: Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" ); default: #ifndef Q3_VM #ifndef NDEBUG // bk0001204 Com_Printf("BG_CanItemBeGrabbed: unknown enum %d\n", item->giType ); #endif #endif break; } return qfalse; } //====================================================================== /* ================ BG_EvaluateTrajectory ================ */ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorCopy( tr->trBase, result ); break; case TR_LINEAR: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = sin( deltaTime * M_PI * 2 ); VectorMA( tr->trBase, phase, tr->trDelta, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { atTime = tr->trTime + tr->trDuration; } deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds if ( deltaTime < 0 ) { deltaTime = 0; } VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime ); break; } } /* ================ BG_EvaluateTrajectoryDelta For determining velocity at a given time ================ */ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorClear( result ); break; case TR_LINEAR: VectorCopy( tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = cos( deltaTime * M_PI * 2 ); // derivative of sin = cos phase *= 0.5; VectorScale( tr->trDelta, phase, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { VectorClear( result ); return; } VectorCopy( tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorCopy( tr->trDelta, result ); result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime ); break; } } char *eventnames[] = { "EV_NONE", "EV_FOOTSTEP", "EV_FOOTSTEP_METAL", "EV_FOOTSPLASH", "EV_FOOTWADE", "EV_SWIM", "EV_STEP_4", "EV_STEP_8", "EV_STEP_12", "EV_STEP_16", "EV_FALL_SHORT", "EV_FALL_MEDIUM", "EV_FALL_FAR", "EV_JUMP_PAD", // boing sound at origin", jump sound on player "EV_JUMP", "EV_WATER_TOUCH", // foot touches "EV_WATER_LEAVE", // foot leaves "EV_WATER_UNDER", // head touches "EV_WATER_CLEAR", // head leaves "EV_ITEM_PICKUP", // normal item pickups are predictable "EV_GLOBAL_ITEM_PICKUP", // powerup / team sounds are broadcast to everyone "EV_NOAMMO", "EV_CHANGE_WEAPON", "EV_FIRE_WEAPON", "EV_USE_ITEM0", "EV_USE_ITEM1", "EV_USE_ITEM2", "EV_USE_ITEM3", "EV_USE_ITEM4", "EV_USE_ITEM5", "EV_USE_ITEM6", "EV_USE_ITEM7", "EV_USE_ITEM8", "EV_USE_ITEM9", "EV_USE_ITEM10", "EV_USE_ITEM11", "EV_USE_ITEM12", "EV_USE_ITEM13", "EV_USE_ITEM14", "EV_USE_ITEM15", "EV_ITEM_RESPAWN", "EV_ITEM_POP", "EV_PLAYER_TELEPORT_IN", "EV_PLAYER_TELEPORT_OUT", "EV_GRENADE_BOUNCE", // eventParm will be the soundindex "EV_GENERAL_SOUND", "EV_GLOBAL_SOUND", // no attenuation "EV_GLOBAL_TEAM_SOUND", "EV_BULLET_HIT_FLESH", "EV_BULLET_HIT_WALL", "EV_MISSILE_HIT", "EV_MISSILE_MISS", "EV_MISSILE_MISS_METAL", "EV_RAILTRAIL", "EV_SHOTGUN", "EV_BULLET", // otherEntity is the shooter "EV_PAIN", "EV_DEATH1", "EV_DEATH2", "EV_DEATH3", "EV_OBITUARY", "EV_POWERUP_QUAD", "EV_POWERUP_BATTLESUIT", "EV_POWERUP_REGEN", "EV_GIB_PLAYER", // gib a previously living player "EV_SCOREPLUM", // score plum //Not all of these are used in baseoa but we keep them to gurantie event numbers between version "EV_PROXIMITY_MINE_STICK", "EV_PROXIMITY_MINE_TRIGGER", "EV_KAMIKAZE", // kamikaze explodes "EV_OBELISKEXPLODE", // obelisk explodes "EV_INVUL_IMPACT", // invulnerability sphere impact "EV_JUICED", // invulnerability juiced effect "EV_LIGHTNINGBOLT", // lightning bolt bounced of invulnerability sphere "EV_DEBUG_LINE", "EV_STOPLOOPINGSOUND", "EV_TAUNT" }; /* =============== BG_AddPredictableEventToPlayerstate Handles the sequence numbers =============== */ void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) { #ifdef _DEBUG { char buf[256]; trap_Cvar_VariableStringBuffer("showevents", buf, sizeof(buf)); if ( atof(buf) != 0 ) { #ifdef QAGAME Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #else Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #endif } } #endif ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent; ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm; ps->eventSequence++; } /* ======================== BG_TouchJumpPad ======================== */ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { vec3_t angles; float p; int effectNum; // spectators don't use jump pads if ( ps->pm_type != PM_NORMAL ) { return; } // flying characters don't hit bounce pads if ( ps->powerups[PW_FLIGHT] ) { return; } // if we didn't hit this same jumppad the previous frame // then don't play the event sound again if we are in a fat trigger if ( ps->jumppad_ent != jumppad->number ) { vectoangles( jumppad->origin2, angles); p = fabs( AngleNormalize180( angles[PITCH] ) ); if( p < 45 ) { effectNum = 0; } else { effectNum = 1; } BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); } // remember hitting this jumppad this frame ps->jumppad_ent = jumppad->number; ps->jumppad_frame = ps->pmove_framecount; // give the player the velocity from the jumppad VectorCopy( jumppad->origin2, ps->velocity ); } /* ======================== BG_PlayerStateToEntityState This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_INTERPOLATE; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction VectorCopy( ps->velocity, s->pos.trDelta ); s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; } /* ======================== BG_PlayerStateToEntityStateExtraPolate This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_LINEAR_STOP; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction and linear prediction VectorCopy( ps->velocity, s->pos.trDelta ); // set the time for linear prediction s->pos.trTime = time; // set maximum extra polation time s->pos.trDuration = 50; // 1000 / sv_fps (default = 20) s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; } /* ============ BG_TeamName KK-OAX Copied from Tremulous ============ */ char *BG_TeamName( team_t team ) { if( team == TEAM_NONE ) return "spectator"; if( team == TEAM_RED ) return "Red"; if( team == TEAM_BLUE ) return "Blue"; if( team == TEAM_FREE ) return "Free For All"; return ""; } openarena_0.8.8.orig/code/game/inv.h0000644000175000017500000001256511656310264016027 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #define INVENTORY_NONE 0 //armor #define INVENTORY_ARMOR 1 //weapons #define INVENTORY_GAUNTLET 4 #define INVENTORY_SHOTGUN 5 #define INVENTORY_MACHINEGUN 6 #define INVENTORY_GRENADELAUNCHER 7 #define INVENTORY_ROCKETLAUNCHER 8 #define INVENTORY_LIGHTNING 9 #define INVENTORY_RAILGUN 10 #define INVENTORY_PLASMAGUN 11 #define INVENTORY_BFG10K 13 #define INVENTORY_GRAPPLINGHOOK 14 #define INVENTORY_NAILGUN 15 #define INVENTORY_PROXLAUNCHER 16 #define INVENTORY_CHAINGUN 17 //ammo #define INVENTORY_SHELLS 18 #define INVENTORY_BULLETS 19 #define INVENTORY_GRENADES 20 #define INVENTORY_CELLS 21 #define INVENTORY_LIGHTNINGAMMO 22 #define INVENTORY_ROCKETS 23 #define INVENTORY_SLUGS 24 #define INVENTORY_BFGAMMO 25 #define INVENTORY_NAILS 26 #define INVENTORY_MINES 27 #define INVENTORY_BELT 28 //powerups #define INVENTORY_HEALTH 29 #define INVENTORY_TELEPORTER 30 #define INVENTORY_MEDKIT 31 #define INVENTORY_KAMIKAZE 32 #define INVENTORY_PORTAL 33 #define INVENTORY_INVULNERABILITY 34 #define INVENTORY_QUAD 35 #define INVENTORY_ENVIRONMENTSUIT 36 #define INVENTORY_HASTE 37 #define INVENTORY_INVISIBILITY 38 #define INVENTORY_REGEN 39 #define INVENTORY_FLIGHT 40 #define INVENTORY_SCOUT 41 #define INVENTORY_GUARD 42 #define INVENTORY_DOUBLER 43 #define INVENTORY_AMMOREGEN 44 #define INVENTORY_REDFLAG 45 #define INVENTORY_BLUEFLAG 46 #define INVENTORY_NEUTRALFLAG 47 #define INVENTORY_REDCUBE 48 #define INVENTORY_BLUECUBE 49 //Elimination mod: Domination inventory #define INVENTORY_POINTWHITE 50 #define INVENTORY_POINTRED 51 #define INVENTORY_POINTBLUE 52 //enemy stuff #define ENEMY_HORIZONTAL_DIST 200 #define ENEMY_HEIGHT 201 #define NUM_VISIBLE_ENEMIES 202 #define NUM_VISIBLE_TEAMMATES 203 // if running the mission pack #ifdef MISSIONPACK //#error "running mission pack" #endif //item numbers (make sure they are in sync with bg_itemlist in bg_misc.c) #define MODELINDEX_ARMORSHARD 1 #define MODELINDEX_ARMORCOMBAT 2 #define MODELINDEX_ARMORBODY 3 #define MODELINDEX_HEALTHSMALL 4 #define MODELINDEX_HEALTH 5 #define MODELINDEX_HEALTHLARGE 6 #define MODELINDEX_HEALTHMEGA 7 #define MODELINDEX_GAUNTLET 8 #define MODELINDEX_SHOTGUN 9 #define MODELINDEX_MACHINEGUN 10 #define MODELINDEX_GRENADELAUNCHER 11 #define MODELINDEX_ROCKETLAUNCHER 12 #define MODELINDEX_LIGHTNING 13 #define MODELINDEX_RAILGUN 14 #define MODELINDEX_PLASMAGUN 15 #define MODELINDEX_BFG10K 16 #define MODELINDEX_GRAPPLINGHOOK 17 #define MODELINDEX_SHELLS 18 #define MODELINDEX_BULLETS 19 #define MODELINDEX_GRENADES 20 #define MODELINDEX_CELLS 21 #define MODELINDEX_LIGHTNINGAMMO 22 #define MODELINDEX_ROCKETS 23 #define MODELINDEX_SLUGS 24 #define MODELINDEX_BFGAMMO 25 #define MODELINDEX_TELEPORTER 26 #define MODELINDEX_MEDKIT 27 #define MODELINDEX_QUAD 28 #define MODELINDEX_ENVIRONMENTSUIT 29 #define MODELINDEX_HASTE 30 #define MODELINDEX_INVISIBILITY 31 #define MODELINDEX_REGEN 32 #define MODELINDEX_FLIGHT 33 #define MODELINDEX_REDFLAG 34 #define MODELINDEX_BLUEFLAG 35 // mission pack only defines #define MODELINDEX_KAMIKAZE 36 #define MODELINDEX_PORTAL 37 #define MODELINDEX_INVULNERABILITY 38 #define MODELINDEX_NAILS 39 #define MODELINDEX_MINES 40 #define MODELINDEX_BELT 41 #define MODELINDEX_SCOUT 42 #define MODELINDEX_GUARD 43 #define MODELINDEX_DOUBLER 44 #define MODELINDEX_AMMOREGEN 45 #define MODELINDEX_NEUTRALFLAG 46 #define MODELINDEX_REDCUBE 47 #define MODELINDEX_BLUECUBE 48 #define MODELINDEX_NAILGUN 49 #define MODELINDEX_PROXLAUNCHER 50 #define MODELINDEX_CHAINGUN 51 //Elimination mod: Double Domination and Standard Domination #define MODELINDEX_POINTABLUE 52 #define MODELINDEX_POINTBBLUE 53 #define MODELINDEX_POINTARED 54 #define MODELINDEX_POINTBRED 55 #define MODELINDEX_POINTAWHITE 56 #define MODELINDEX_POINTBWHITE 57 #define MODELINDEX_POINTWHITE 58 #define MODELINDEX_POINTRED 59 #define MODELINDEX_POINTBLUE 60 // #define WEAPONINDEX_GAUNTLET 1 #define WEAPONINDEX_MACHINEGUN 2 #define WEAPONINDEX_SHOTGUN 3 #define WEAPONINDEX_GRENADE_LAUNCHER 4 #define WEAPONINDEX_ROCKET_LAUNCHER 5 #define WEAPONINDEX_LIGHTNING 6 #define WEAPONINDEX_RAILGUN 7 #define WEAPONINDEX_PLASMAGUN 8 #define WEAPONINDEX_BFG 9 #define WEAPONINDEX_GRAPPLING_HOOK 10 #define WEAPONINDEX_NAILGUN 11 #define WEAPONINDEX_PROXLAUNCHER 12 #define WEAPONINDEX_CHAINGUN 13 openarena_0.8.8.orig/code/game/ai_chat.h0000644000175000017500000000363411656310264016620 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_chat.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ // int BotChat_EnterGame(bot_state_t *bs); // int BotChat_ExitGame(bot_state_t *bs); // int BotChat_StartLevel(bot_state_t *bs); // int BotChat_EndLevel(bot_state_t *bs); // int BotChat_HitTalking(bot_state_t *bs); // int BotChat_HitNoDeath(bot_state_t *bs); // int BotChat_HitNoKill(bot_state_t *bs); // int BotChat_Death(bot_state_t *bs); // int BotChat_Kill(bot_state_t *bs); // int BotChat_EnemySuicide(bot_state_t *bs); // int BotChat_Random(bot_state_t *bs); // time the selected chat takes to type in float BotChatTime(bot_state_t *bs); // returns true if the bot can chat at the current position int BotValidChatPosition(bot_state_t *bs); // test the initial bot chats void BotChatTest(bot_state_t *bs); openarena_0.8.8.orig/code/game/g_fileops.c0000644000175000017500000000565711656310264017201 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Karl Kuglin Copyright (C) 2004-2006 Tony J. White This file is part of the Open Arena source code. Portions copied/modified from Tremulous under GPL version 2 including any later versions. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" /* ================ readFile_int This parses an integer for the "tag" specified (cnf) ================ */ void readFile_int( char **cnf, int *v ) { char *t; //COM_MatchToken(cnf, "="); t = COM_ParseExt( cnf, qfalse ); if( !strcmp( t, "=" ) ) { t = COM_ParseExt( cnf, qfalse ); } else { COM_ParseWarning( "expected '=' before \"%s\"", t ); } *v = atoi( t ); } /* ================ readFile_string This parses a literal string for the "tag" specified Color characters and escape sequences are parsed as well. ================ */ void readFile_string( char **cnf, char *s, int size ) { char *t; //COM_MatchToken(cnf, "="); s[ 0 ] = '\0'; t = COM_ParseExt( cnf, qfalse ); if( strcmp( t, "=" ) ) { COM_ParseWarning( "expected '=' before \"%s\"", t ); Q_strncpyz( s, t, size ); } while( 1 ) { t = COM_ParseExt( cnf, qfalse ); if( !*t ) break; if( strlen( t ) + strlen( s ) >= size ) break; if( *s ) Q_strcat( s, size, " " ); Q_strcat( s, size, t ); } } /* ================ writeFile_int This writes an integer to the file. Since there is no logic as to where it writes, it must be called "just-in-time." ================ */ void writeFile_int( int v, fileHandle_t f ) { char buf[ 32 ]; Com_sprintf( buf, sizeof( buf ), "%d", v ); trap_FS_Write( buf, strlen( buf ), f ); trap_FS_Write( "\n", 1, f ); } /* ================ writeFile_string This writes a string to the file. Since there is no logic as to where it writes, it must be called "just-in-time." ================ */ void writeFile_string( char *s, fileHandle_t f ) { char buf[ MAX_STRING_CHARS ]; buf[ 0 ] = '\0'; if( s[ 0 ] ) { //Q_strcat(buf, sizeof(buf), s); Q_strncpyz( buf, s, sizeof( buf ) ); trap_FS_Write( buf, strlen( buf ), f ); } trap_FS_Write( "\n", 1, f ); } openarena_0.8.8.orig/code/game/ai_main.h0000644000175000017500000003411111656310264016617 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_main.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ //#define DEBUG #define CTF #define MAX_ITEMS 256 //bot flags #define BFL_STRAFERIGHT 1 //strafe to the right #define BFL_ATTACKED 2 //bot has attacked last ai frame #define BFL_ATTACKJUMPED 4 //bot jumped during attack last frame #define BFL_AIMATENEMY 8 //bot aimed at the enemy this frame #define BFL_AVOIDRIGHT 16 //avoid obstacles by going to the right #define BFL_IDEALVIEWSET 32 //bot has ideal view angles set #define BFL_FIGHTSUICIDAL 64 //bot is in a suicidal fight //long term goal types #define LTG_TEAMHELP 1 //help a team mate #define LTG_TEAMACCOMPANY 2 //accompany a team mate #define LTG_DEFENDKEYAREA 3 //defend a key area #define LTG_GETFLAG 4 //get the enemy flag #define LTG_RUSHBASE 5 //rush to the base #define LTG_RETURNFLAG 6 //return the flag #define LTG_CAMP 7 //camp somewhere #define LTG_CAMPORDER 8 //ordered to camp somewhere #define LTG_PATROL 9 //patrol #define LTG_GETITEM 10 //get an item #define LTG_KILL 11 //kill someone #define LTG_HARVEST 12 //harvest skulls #define LTG_ATTACKENEMYBASE 13 //attack the enemy base #define LTG_MAKELOVE_UNDER 14 #define LTG_MAKELOVE_ONTOP 15 //Long term DD goals #define LTG_POINTA 16 //Take/Defend point A #define LTG_POINTB 17 //Take/Defend point B //Long term DD goals #define LTG_DOMROAM 18 //Go for a non taken point. #define LTG_DOMHOLD 19 //Pick a point and hold it. //some goal dedication times #define TEAM_HELP_TIME 60 //1 minute teamplay help time #define TEAM_ACCOMPANY_TIME 600 //10 minutes teamplay accompany time #define TEAM_DEFENDKEYAREA_TIME 600 //10 minutes ctf defend base time #define TEAM_CAMP_TIME 600 //10 minutes camping time #define TEAM_PATROL_TIME 600 //10 minutes patrolling time #define TEAM_LEAD_TIME 600 //10 minutes taking the lead #define TEAM_GETITEM_TIME 60 //1 minute #define TEAM_KILL_SOMEONE 180 //3 minute to kill someone #define TEAM_ATTACKENEMYBASE_TIME 600 //10 minutes #define TEAM_HARVEST_TIME 120 //2 minutes #define CTF_GETFLAG_TIME 600 //10 minutes ctf get flag time #define CTF_RUSHBASE_TIME 120 //2 minutes ctf rush base time #define CTF_RETURNFLAG_TIME 180 //3 minutes to return the flag #define CTF_ROAM_TIME 60 //1 minute ctf roam time //Time for Double Domination tasks #define DD_POINTA 600 #define DD_POINTB 600 //patrol flags #define PATROL_LOOP 1 #define PATROL_REVERSE 2 #define PATROL_BACK 4 //teamplay task preference #define TEAMTP_DEFENDER 1 #define TEAMTP_ATTACKER 2 //CTF strategy #define CTFS_AGRESSIVE 1 //copied from the aas file header #define PRESENCE_NONE 1 #define PRESENCE_NORMAL 2 #define PRESENCE_CROUCH 4 // #define MAX_PROXMINES 64 //check points typedef struct bot_waypoint_s { int inuse; char name[32]; bot_goal_t goal; struct bot_waypoint_s *next, *prev; } bot_waypoint_t; #define MAX_ACTIVATESTACK 8 #define MAX_ACTIVATEAREAS 32 typedef struct bot_activategoal_s { int inuse; bot_goal_t goal; //goal to activate (buttons etc.) float time; //time to activate something float start_time; //time starting to activate something float justused_time; //time the goal was used int shoot; //true if bot has to shoot to activate int weapon; //weapon to be used for activation vec3_t target; //target to shoot at to activate something vec3_t origin; //origin of the blocking entity to activate int areas[MAX_ACTIVATEAREAS]; //routing areas disabled by blocking entity int numareas; //number of disabled routing areas int areasdisabled; //true if the areas are disabled for the routing struct bot_activategoal_s *next; //next activate goal on stack } bot_activategoal_t; //bot state typedef struct bot_state_s { int inuse; //true if this state is used by a bot client int botthink_residual; //residual for the bot thinks int client; //client number of the bot int entitynum; //entity number of the bot playerState_t cur_ps; //current player state int last_eFlags; //last ps flags usercmd_t lastucmd; //usercmd from last frame int entityeventTime[1024]; //last entity event time // bot_settings_t settings; //several bot settings int (*ainode)(struct bot_state_s *bs); //current AI node float thinktime; //time the bot thinks this frame vec3_t origin; //origin of the bot vec3_t velocity; //velocity of the bot int presencetype; //presence type of the bot vec3_t eye; //eye coordinates of the bot int areanum; //the number of the area the bot is in int inventory[MAX_ITEMS]; //string with items amounts the bot has int tfl; //the travel flags the bot uses int flags; //several flags int respawn_wait; //wait until respawned int lasthealth; //health value previous frame int lastkilledplayer; //last killed player int lastkilledby; //player that last killed this bot int botdeathtype; //the death type of the bot int enemydeathtype; //the death type of the enemy int botsuicide; //true when the bot suicides int enemysuicide; //true when the enemy of the bot suicides int setupcount; //true when the bot has just been setup int map_restart; //true when the map is being restarted int entergamechat; //true when the bot used an enter game chat int num_deaths; //number of time this bot died int num_kills; //number of kills of this bot int revenge_enemy; //the revenge enemy int revenge_kills; //number of kills the enemy made int lastframe_health; //health value the last frame int lasthitcount; //number of hits last frame int chatto; //chat to all or team float walker; //walker charactertic float ltime; //local bot time float entergame_time; //time the bot entered the game float ltg_time; //long term goal time float nbg_time; //nearby goal time float respawn_time; //time the bot takes to respawn float respawnchat_time; //time the bot started a chat during respawn float chase_time; //time the bot will chase the enemy float enemyvisible_time; //time the enemy was last visible float check_time; //time to check for nearby items float stand_time; //time the bot is standing still float lastchat_time; //time the bot last selected a chat float kamikaze_time; //time to check for kamikaze usage float invulnerability_time; //time to check for invulnerability usage float standfindenemy_time; //time to find enemy while standing float attackstrafe_time; //time the bot is strafing in one dir float attackcrouch_time; //time the bot will stop crouching float attackchase_time; //time the bot chases during actual attack float attackjump_time; //time the bot jumped during attack float enemysight_time; //time before reacting to enemy float enemydeath_time; //time the enemy died float enemyposition_time; //time the position and velocity of the enemy were stored float defendaway_time; //time away while defending float defendaway_range; //max travel time away from defend area float rushbaseaway_time; //time away from rushing to the base float attackaway_time; //time away from attacking the enemy base float harvestaway_time; //time away from harvesting float ctfroam_time; //time the bot is roaming in ctf float killedenemy_time; //time the bot killed the enemy float arrive_time; //time arrived (at companion) float lastair_time; //last time the bot had air float teleport_time; //last time the bot teleported float camp_time; //last time camped float camp_range; //camp range float weaponchange_time; //time the bot started changing weapons float firethrottlewait_time; //amount of time to wait float firethrottleshoot_time; //amount of time to shoot float notblocked_time; //last time the bot was not blocked float blockedbyavoidspot_time; //time blocked by an avoid spot float predictobstacles_time; //last time the bot predicted obstacles int predictobstacles_goalareanum; //last goal areanum the bot predicted obstacles for vec3_t aimtarget; vec3_t enemyvelocity; //enemy velocity 0.5 secs ago during battle vec3_t enemyorigin; //enemy origin 0.5 secs ago during battle // int kamikazebody; //kamikaze body int proxmines[MAX_PROXMINES]; int numproxmines; // int character; //the bot character int ms; //move state of the bot int gs; //goal state of the bot int cs; //chat state of the bot int ws; //weapon state of the bot // int enemy; //enemy entity number int lastenemyareanum; //last reachability area the enemy was in vec3_t lastenemyorigin; //last origin of the enemy in the reachability area int weaponnum; //current weapon number vec3_t viewangles; //current view angles vec3_t ideal_viewangles; //ideal view angles vec3_t viewanglespeed; // int ltgtype; //long term goal type // team goals int teammate; //team mate involved in this team goal int decisionmaker; //player who decided to go for this goal int ordered; //true if ordered to do something float order_time; //time ordered to do something int owndecision_time; //time the bot made it's own decision bot_goal_t teamgoal; //the team goal bot_goal_t altroutegoal; //alternative route goal float reachedaltroutegoal_time; //time the bot reached the alt route goal float teammessage_time; //time to message team mates what the bot is doing float teamgoal_time; //time to stop helping team mate float teammatevisible_time; //last time the team mate was NOT visible int teamtaskpreference; //team task preference // last ordered team goal int lastgoal_decisionmaker; int lastgoal_ltgtype; int lastgoal_teammate; bot_goal_t lastgoal_teamgoal; // for leading team mates int lead_teammate; //team mate the bot is leading bot_goal_t lead_teamgoal; //team goal while leading float lead_time; //time leading someone float leadvisible_time; //last time the team mate was visible float leadmessage_time; //last time a messaged was sent to the team mate float leadbackup_time; //time backing up towards team mate // char teamleader[32]; //netname of the team leader float askteamleader_time; //time asked for team leader float becometeamleader_time; //time the bot will become the team leader float teamgiveorders_time; //time to give team orders float lastflagcapture_time; //last time a flag was captured int numteammates; //number of team mates int redflagstatus; //0 = at base, 1 = not at base int blueflagstatus; //0 = at base, 1 = not at base int neutralflagstatus; //0 = at base, 1 = our team has flag, 2 = enemy team has flag, 3 = enemy team dropped the flag int flagstatuschanged; //flag status changed int forceorders; //true if forced to give orders int flagcarrier; //team mate carrying the enemy flag int ctfstrategy; //ctf strategy char subteam[32]; //sub team name float formation_dist; //formation team mate intervening space char formation_teammate[16]; //netname of the team mate the bot uses for relative positioning float formation_angle; //angle relative to the formation team mate vec3_t formation_dir; //the direction the formation is moving in vec3_t formation_origin; //origin the bot uses for relative positioning bot_goal_t formation_goal; //formation goal bot_activategoal_t *activatestack; //first activate goal on the stack bot_activategoal_t activategoalheap[MAX_ACTIVATESTACK]; //activate goal heap bot_waypoint_t *checkpoints; //check points bot_waypoint_t *patrolpoints; //patrol points bot_waypoint_t *curpatrolpoint; //current patrol point the bot is going for int patrolflags; //patrol flags } bot_state_t; //resets the whole bot state void BotResetState(bot_state_t *bs); //returns the number of bots in the game int NumBots(void); //returns info about the entity void BotEntityInfo(int entnum, aas_entityinfo_t *info); extern float floattime; #define FloatTime() floattime // from the game source void QDECL BotAI_Print(int type, char *fmt, ...); void QDECL QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ); void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); int BotAI_GetClientState( int clientNum, playerState_t *state ); int BotAI_GetEntityState( int entityNum, entityState_t *state ); int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ); int BotTeamLeader(bot_state_t *bs); openarena_0.8.8.orig/code/game/g_syscalls.asm0000644000175000017500000001413411656310264017721 0ustar smcvsmcvcode equ trap_Printf -1 equ trap_Error -2 equ trap_Milliseconds -3 equ trap_Cvar_Register -4 equ trap_Cvar_Update -5 equ trap_Cvar_Set -6 equ trap_Cvar_VariableIntegerValue -7 equ trap_Cvar_VariableStringBuffer -8 equ trap_Argc -9 equ trap_Argv -10 equ trap_FS_FOpenFile -11 equ trap_FS_Read -12 equ trap_FS_Write -13 equ trap_FS_FCloseFile -14 equ trap_SendConsoleCommand -15 equ trap_LocateGameData -16 equ trap_DropClient -17 equ trap_SendServerCommand -18 equ trap_SetConfigstring -19 equ trap_GetConfigstring -20 equ trap_GetUserinfo -21 equ trap_SetUserinfo -22 equ trap_GetServerinfo -23 equ trap_SetBrushModel -24 equ trap_Trace -25 equ trap_PointContents -26 equ trap_InPVS -27 equ trap_InPVSIgnorePortals -28 equ trap_AdjustAreaPortalState -29 equ trap_AreasConnected -30 equ trap_LinkEntity -31 equ trap_UnlinkEntity -32 equ trap_EntitiesInBox -33 equ trap_EntityContact -34 equ trap_BotAllocateClient -35 equ trap_BotFreeClient -36 equ trap_GetUsercmd -37 equ trap_GetEntityToken -38 equ trap_FS_GetFileList -39 equ trap_DebugPolygonCreate -40 equ trap_DebugPolygonDelete -41 equ trap_RealTime -42 equ trap_SnapVector -43 equ trap_TraceCapsule -44 equ trap_EntityContactCapsule -45 equ trap_FS_Seek -46 equ memset -101 equ memcpy -102 equ strncpy -103 equ sin -104 equ cos -105 equ atan2 -106 equ sqrt -107 equ floor -111 equ ceil -112 equ testPrintInt -113 equ testPrintFloat -114 equ trap_BotLibSetup -201 equ trap_BotLibShutdown -202 equ trap_BotLibVarSet -203 equ trap_BotLibVarGet -204 equ trap_BotLibDefine -205 equ trap_BotLibStartFrame -206 equ trap_BotLibLoadMap -207 equ trap_BotLibUpdateEntity -208 equ trap_BotLibTest -209 equ trap_BotGetSnapshotEntity -210 equ trap_BotGetServerCommand -211 equ trap_BotUserCommand -212 equ trap_AAS_EnableRoutingArea -301 equ trap_AAS_BBoxAreas -302 equ trap_AAS_AreaInfo -303 equ trap_AAS_EntityInfo -304 equ trap_AAS_Initialized -305 equ trap_AAS_PresenceTypeBoundingBox -306 equ trap_AAS_Time -307 equ trap_AAS_PointAreaNum -308 equ trap_AAS_TraceAreas -309 equ trap_AAS_PointContents -310 equ trap_AAS_NextBSPEntity -311 equ trap_AAS_ValueForBSPEpairKey -312 equ trap_AAS_VectorForBSPEpairKey -313 equ trap_AAS_FloatForBSPEpairKey -314 equ trap_AAS_IntForBSPEpairKey -315 equ trap_AAS_AreaReachability -316 equ trap_AAS_AreaTravelTimeToGoalArea -317 equ trap_AAS_Swimming -318 equ trap_AAS_PredictClientMovement -319 equ trap_EA_Say -401 equ trap_EA_SayTeam -402 equ trap_EA_Command -403 equ trap_EA_Action -404 equ trap_EA_Gesture -405 equ trap_EA_Talk -406 equ trap_EA_Attack -407 equ trap_EA_Use -408 equ trap_EA_Respawn -409 equ trap_EA_Crouch -410 equ trap_EA_MoveUp -411 equ trap_EA_MoveDown -412 equ trap_EA_MoveForward -413 equ trap_EA_MoveBack -414 equ trap_EA_MoveLeft -415 equ trap_EA_MoveRight -416 equ trap_EA_SelectWeapon -417 equ trap_EA_Jump -418 equ trap_EA_DelayedJump -419 equ trap_EA_Move -420 equ trap_EA_View -421 equ trap_EA_EndRegular -422 equ trap_EA_GetInput -423 equ trap_EA_ResetInput -424 equ trap_BotLoadCharacter -501 equ trap_BotFreeCharacter -502 equ trap_Characteristic_Float -503 equ trap_Characteristic_BFloat -504 equ trap_Characteristic_Integer -505 equ trap_Characteristic_BInteger -506 equ trap_Characteristic_String -507 equ trap_BotAllocChatState -508 equ trap_BotFreeChatState -509 equ trap_BotQueueConsoleMessage -510 equ trap_BotRemoveConsoleMessage -511 equ trap_BotNextConsoleMessage -512 equ trap_BotNumConsoleMessages -513 equ trap_BotInitialChat -514 equ trap_BotReplyChat -515 equ trap_BotChatLength -516 equ trap_BotEnterChat -517 equ trap_StringContains -518 equ trap_BotFindMatch -519 equ trap_BotMatchVariable -520 equ trap_UnifyWhiteSpaces -521 equ trap_BotReplaceSynonyms -522 equ trap_BotLoadChatFile -523 equ trap_BotSetChatGender -524 equ trap_BotSetChatName -525 equ trap_BotResetGoalState -526 equ trap_BotResetAvoidGoals -527 equ trap_BotPushGoal -528 equ trap_BotPopGoal -529 equ trap_BotEmptyGoalStack -530 equ trap_BotDumpAvoidGoals -531 equ trap_BotDumpGoalStack -532 equ trap_BotGoalName -533 equ trap_BotGetTopGoal -534 equ trap_BotGetSecondGoal -535 equ trap_BotChooseLTGItem -536 equ trap_BotChooseNBGItem -537 equ trap_BotTouchingGoal -538 equ trap_BotItemGoalInVisButNotVisible -539 equ trap_BotGetLevelItemGoal -540 equ trap_BotAvoidGoalTime -541 equ trap_BotInitLevelItems -542 equ trap_BotUpdateEntityItems -543 equ trap_BotLoadItemWeights -544 equ trap_BotFreeItemWeights -546 equ trap_BotSaveGoalFuzzyLogic -546 equ trap_BotAllocGoalState -547 equ trap_BotFreeGoalState -548 equ trap_BotResetMoveState -549 equ trap_BotMoveToGoal -550 equ trap_BotMoveInDirection -551 equ trap_BotResetAvoidReach -552 equ trap_BotResetLastAvoidReach -553 equ trap_BotReachabilityArea -554 equ trap_BotMovementViewTarget -555 equ trap_BotAllocMoveState -556 equ trap_BotFreeMoveState -557 equ trap_BotInitMoveState -558 equ trap_BotChooseBestFightWeapon -559 equ trap_BotGetWeaponInfo -560 equ trap_BotLoadWeaponWeights -561 equ trap_BotAllocWeaponState -562 equ trap_BotFreeWeaponState -563 equ trap_BotResetWeaponState -564 equ trap_GeneticParentsAndChildSelection -565 equ trap_BotInterbreedGoalFuzzyLogic -566 equ trap_BotMutateGoalFuzzyLogic -567 equ trap_BotGetNextCampSpotGoal -568 equ trap_BotGetMapLocationGoal -569 equ trap_BotNumInitialChats -570 equ trap_BotGetChatMessage -571 equ trap_BotRemoveFromAvoidGoals -572 equ trap_BotPredictVisiblePosition -573 equ trap_BotSetAvoidGoalTime -574 equ trap_BotAddAvoidSpot -575 equ trap_AAS_AlternativeRouteGoals -576 equ trap_AAS_PredictRoute -577 equ trap_AAS_PointReachabilityAreaIndex -578 equ trap_BotLibLoadSource -579 equ trap_BotLibFreeSource -580 equ trap_BotLibReadToken -581 equ trap_BotLibSourceFileAndLine -582 openarena_0.8.8.orig/code/game/g_mover.c0000644000175000017500000012260211656310264016656 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" /* =============================================================================== PUSHMOVE =============================================================================== */ void MatchTeam( gentity_t *teamLeader, int moverState, int time ); typedef struct { gentity_t *ent; vec3_t origin; vec3_t angles; float deltayaw; } pushed_t; pushed_t pushed[MAX_GENTITIES], *pushed_p; /* ============ G_TestEntityPosition ============ */ gentity_t *G_TestEntityPosition( gentity_t *ent ) { trace_t tr; int mask; if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID; } if ( ent->client ) { trap_Trace( &tr, ent->client->ps.origin, ent->r.mins, ent->r.maxs, ent->client->ps.origin, ent->s.number, mask ); } else { trap_Trace( &tr, ent->s.pos.trBase, ent->r.mins, ent->r.maxs, ent->s.pos.trBase, ent->s.number, mask ); } if (tr.startsolid) return &g_entities[ tr.entityNum ]; return NULL; } /* ================ G_CreateRotationMatrix ================ */ void G_CreateRotationMatrix(vec3_t angles, vec3_t matrix[3]) { AngleVectors(angles, matrix[0], matrix[1], matrix[2]); VectorInverse(matrix[1]); } /* ================ G_TransposeMatrix ================ */ void G_TransposeMatrix(vec3_t matrix[3], vec3_t transpose[3]) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { transpose[i][j] = matrix[j][i]; } } } /* ================ G_RotatePoint ================ */ void G_RotatePoint(vec3_t point, vec3_t matrix[3]) { vec3_t tvec; VectorCopy(point, tvec); point[0] = DotProduct(matrix[0], tvec); point[1] = DotProduct(matrix[1], tvec); point[2] = DotProduct(matrix[2], tvec); } /* ================== G_TryPushingEntity Returns qfalse if the move is blocked ================== */ qboolean G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, vec3_t amove ) { vec3_t matrix[3], transpose[3]; vec3_t org, org2, move2; gentity_t *block; // EF_MOVER_STOP will just stop when contacting another entity // instead of pushing it, but entities can still ride on top of it if ( ( pusher->s.eFlags & EF_MOVER_STOP ) && check->s.groundEntityNum != pusher->s.number ) { return qfalse; } // save off the old position if (pushed_p > &pushed[MAX_GENTITIES]) { G_Error( "pushed_p > &pushed[MAX_GENTITIES]" ); } pushed_p->ent = check; VectorCopy (check->s.pos.trBase, pushed_p->origin); VectorCopy (check->s.apos.trBase, pushed_p->angles); if ( check->client ) { pushed_p->deltayaw = check->client->ps.delta_angles[YAW]; VectorCopy (check->client->ps.origin, pushed_p->origin); } pushed_p++; // try moving the contacted entity // figure movement due to the pusher's amove G_CreateRotationMatrix( amove, transpose ); G_TransposeMatrix( transpose, matrix ); if ( check->client ) { VectorSubtract (check->client->ps.origin, pusher->r.currentOrigin, org); } else { VectorSubtract (check->s.pos.trBase, pusher->r.currentOrigin, org); } VectorCopy( org, org2 ); G_RotatePoint( org2, matrix ); VectorSubtract (org2, org, move2); // add movement VectorAdd (check->s.pos.trBase, move, check->s.pos.trBase); VectorAdd (check->s.pos.trBase, move2, check->s.pos.trBase); if ( check->client ) { VectorAdd (check->client->ps.origin, move, check->client->ps.origin); VectorAdd (check->client->ps.origin, move2, check->client->ps.origin); // make sure the client's view rotates when on a rotating mover check->client->ps.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]); } // may have pushed them off an edge if ( check->s.groundEntityNum != pusher->s.number ) { check->s.groundEntityNum = -1; } block = G_TestEntityPosition( check ); if (!block) { // pushed ok if ( check->client ) { VectorCopy( check->client->ps.origin, check->r.currentOrigin ); } else { VectorCopy( check->s.pos.trBase, check->r.currentOrigin ); } trap_LinkEntity (check); return qtrue; } // if it is ok to leave in the old position, do it // this is only relevent for riding entities, not pushed // Sliding trapdoors can cause this. VectorCopy( (pushed_p-1)->origin, check->s.pos.trBase); if ( check->client ) { VectorCopy( (pushed_p-1)->origin, check->client->ps.origin); } VectorCopy( (pushed_p-1)->angles, check->s.apos.trBase ); block = G_TestEntityPosition (check); if ( !block ) { check->s.groundEntityNum = -1; pushed_p--; return qtrue; } // blocked return qfalse; } /* ================== G_CheckProxMinePosition ================== */ qboolean G_CheckProxMinePosition( gentity_t *check ) { vec3_t start, end; trace_t tr; VectorMA(check->s.pos.trBase, 0.125, check->movedir, start); VectorMA(check->s.pos.trBase, 2, check->movedir, end); trap_Trace( &tr, start, NULL, NULL, end, check->s.number, MASK_SOLID ); if (tr.startsolid || tr.fraction < 1) return qfalse; return qtrue; } /* ================== G_TryPushingProxMine ================== */ qboolean G_TryPushingProxMine( gentity_t *check, gentity_t *pusher, vec3_t move, vec3_t amove ) { vec3_t forward, right, up; vec3_t org, org2, move2; int ret; // we need this for pushing things later VectorSubtract (vec3_origin, amove, org); AngleVectors (org, forward, right, up); // try moving the contacted entity VectorAdd (check->s.pos.trBase, move, check->s.pos.trBase); // figure movement due to the pusher's amove VectorSubtract (check->s.pos.trBase, pusher->r.currentOrigin, org); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move2); VectorAdd (check->s.pos.trBase, move2, check->s.pos.trBase); ret = G_CheckProxMinePosition( check ); if (ret) { VectorCopy( check->s.pos.trBase, check->r.currentOrigin ); trap_LinkEntity (check); } return ret; } void G_ExplodeMissile( gentity_t *ent ); /* ============ G_MoverPush Objects need to be moved back on a failed push, otherwise riders would continue to slide. If qfalse is returned, *obstacle will be the blocking entity ============ */ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) { int i, e; gentity_t *check; vec3_t mins, maxs; pushed_t *p; int entityList[MAX_GENTITIES]; int listedEntities; vec3_t totalMins, totalMaxs; *obstacle = NULL; // mins/maxs are the bounds at the destination // totalMins / totalMaxs are the bounds for the entire move if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2] || amove[0] || amove[1] || amove[2] ) { float radius; radius = RadiusFromBounds( pusher->r.mins, pusher->r.maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = pusher->r.currentOrigin[i] + move[i] - radius; maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius; totalMins[i] = mins[i] - move[i]; totalMaxs[i] = maxs[i] - move[i]; } } else { for (i=0 ; i<3 ; i++) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } VectorCopy( pusher->r.absmin, totalMins ); VectorCopy( pusher->r.absmax, totalMaxs ); for (i=0 ; i<3 ; i++) { if ( move[i] > 0 ) { totalMaxs[i] += move[i]; } else { totalMins[i] += move[i]; } } } // unlink the pusher so we don't get it in the entityList trap_UnlinkEntity( pusher ); listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES ); // move the pusher to it's final position VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin ); VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles ); trap_LinkEntity( pusher ); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { check = &g_entities[ entityList[ e ] ]; if ( check->s.eType == ET_MISSILE ) { // if it is a prox mine if ( !strcmp(check->classname, "prox mine") ) { // if this prox mine is attached to this mover try to move it with the pusher if ( check->enemy == pusher ) { if (!G_TryPushingProxMine( check, pusher, move, amove )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } else { //check if the prox mine is crushed by the mover if (!G_CheckProxMinePosition( check )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } continue; } } // only push items and players if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) { continue; } // if the entity is standing on the pusher, it will definitely be moved if ( check->s.groundEntityNum != pusher->s.number ) { // see if the ent needs to be tested if ( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position // this does allow a fast moving object to pass through a thin entity... if (!G_TestEntityPosition (check)) { continue; } } // the entity needs to be pushed if ( G_TryPushingEntity( check, pusher, move, amove ) ) { continue; } // the move was blocked an entity // bobbing entities are instant-kill and never get blocked if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) { G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH ); continue; } // save off the obstacle so we can call the block function (crush, etc) *obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for ( p=pushed_p-1 ; p>=pushed ; p-- ) { VectorCopy (p->origin, p->ent->s.pos.trBase); VectorCopy (p->angles, p->ent->s.apos.trBase); if ( p->ent->client ) { p->ent->client->ps.delta_angles[YAW] = p->deltayaw; VectorCopy (p->origin, p->ent->client->ps.origin); } trap_LinkEntity (p->ent); } return qfalse; } return qtrue; } /* ================= G_MoverTeam ================= */ void G_MoverTeam( gentity_t *ent ) { vec3_t move, amove; gentity_t *part, *obstacle; vec3_t origin, angles; obstacle = NULL; // make sure all team slaves can move before commiting // any moves or calling any think functions // if the move is blocked, all moved objects will be backed out pushed_p = pushed; for (part = ent ; part ; part=part->teamchain) { // get current position BG_EvaluateTrajectory( &part->s.pos, level.time, origin ); BG_EvaluateTrajectory( &part->s.apos, level.time, angles ); VectorSubtract( origin, part->r.currentOrigin, move ); VectorSubtract( angles, part->r.currentAngles, amove ); if ( !G_MoverPush( part, move, amove, &obstacle ) ) { break; // move was blocked } } if (part) { // go back to the previous position for ( part = ent ; part ; part = part->teamchain ) { part->s.pos.trTime += level.time - level.previousTime; part->s.apos.trTime += level.time - level.previousTime; BG_EvaluateTrajectory( &part->s.pos, level.time, part->r.currentOrigin ); BG_EvaluateTrajectory( &part->s.apos, level.time, part->r.currentAngles ); trap_LinkEntity( part ); } // if the pusher has a "blocked" function, call it if (ent->blocked) { ent->blocked( ent, obstacle ); } return; } // the move succeeded for ( part = ent ; part ; part = part->teamchain ) { // call the reached function if time is at or past end point if ( part->s.pos.trType == TR_LINEAR_STOP ) { if ( level.time >= part->s.pos.trTime + part->s.pos.trDuration ) { if ( part->reached ) { part->reached( part ); } } } } } /* ================ G_RunMover ================ */ void G_RunMover( gentity_t *ent ) { // if not a team captain, don't do anything, because // the captain will handle everything if ( ent->flags & FL_TEAMSLAVE ) { return; } // if stationary at one of the positions, don't move anything if ( ent->s.pos.trType != TR_STATIONARY || ent->s.apos.trType != TR_STATIONARY ) { G_MoverTeam( ent ); } // check think function G_RunThink( ent ); } /* ============================================================================ GENERAL MOVERS Doors, plats, and buttons are all binary (two position) movers Pos1 is "at rest", pos2 is "activated" ============================================================================ */ /* =============== SetMoverState =============== */ void SetMoverState( gentity_t *ent, moverState_t moverState, int time ) { vec3_t delta; float f; ent->moverState = moverState; ent->s.pos.trTime = time; switch( moverState ) { case MOVER_POS1: VectorCopy( ent->pos1, ent->s.pos.trBase ); ent->s.pos.trType = TR_STATIONARY; break; case MOVER_POS2: VectorCopy( ent->pos2, ent->s.pos.trBase ); ent->s.pos.trType = TR_STATIONARY; break; case MOVER_1TO2: VectorCopy( ent->pos1, ent->s.pos.trBase ); VectorSubtract( ent->pos2, ent->pos1, delta ); f = 1000.0 / ent->s.pos.trDuration; VectorScale( delta, f, ent->s.pos.trDelta ); ent->s.pos.trType = TR_LINEAR_STOP; break; case MOVER_2TO1: VectorCopy( ent->pos2, ent->s.pos.trBase ); VectorSubtract( ent->pos1, ent->pos2, delta ); f = 1000.0 / ent->s.pos.trDuration; VectorScale( delta, f, ent->s.pos.trDelta ); ent->s.pos.trType = TR_LINEAR_STOP; break; } BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin ); trap_LinkEntity( ent ); } /* ================ MatchTeam All entities in a mover team will move from pos1 to pos2 in the same amount of time ================ */ void MatchTeam( gentity_t *teamLeader, int moverState, int time ) { gentity_t *slave; for ( slave = teamLeader ; slave ; slave = slave->teamchain ) { SetMoverState( slave, moverState, time ); } } /* ================ ReturnToPos1 ================ */ void ReturnToPos1( gentity_t *ent ) { MatchTeam( ent, MOVER_2TO1, level.time ); // looping sound ent->s.loopSound = ent->soundLoop; // starting sound if ( ent->sound2to1 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound2to1 ); } } /* ================ Reached_BinaryMover ================ */ void Reached_BinaryMover( gentity_t *ent ) { // stop the looping sound ent->s.loopSound = ent->soundLoop; if ( ent->moverState == MOVER_1TO2 ) { // reached pos2 SetMoverState( ent, MOVER_POS2, level.time ); // play sound if ( ent->soundPos2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos2 ); } // return to pos1 after a delay ent->think = ReturnToPos1; ent->nextthink = level.time + ent->wait; // fire targets if ( !ent->activator ) { ent->activator = ent; } G_UseTargets( ent, ent->activator ); } else if ( ent->moverState == MOVER_2TO1 ) { // reached pos1 SetMoverState( ent, MOVER_POS1, level.time ); // play sound if ( ent->soundPos1 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos1 ); } // close areaportals if ( ent->teammaster == ent || !ent->teammaster ) { trap_AdjustAreaPortalState( ent, qfalse ); } } else { G_Error( "Reached_BinaryMover: bad moverState" ); } } /* ================ Use_BinaryMover ================ */ void Use_BinaryMover( gentity_t *ent, gentity_t *other, gentity_t *activator ) { int total; int partial; // only the master should be used if ( ent->flags & FL_TEAMSLAVE ) { Use_BinaryMover( ent->teammaster, other, activator ); return; } ent->activator = activator; if ( ent->moverState == MOVER_POS1 ) { // start moving 50 msec later, becase if this was player // triggered, level.time hasn't been advanced yet MatchTeam( ent, MOVER_1TO2, level.time + 50 ); // starting sound if ( ent->sound1to2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound1to2 ); } // looping sound ent->s.loopSound = ent->soundLoop; // open areaportal if ( ent->teammaster == ent || !ent->teammaster ) { trap_AdjustAreaPortalState( ent, qtrue ); } return; } // if all the way up, just delay before coming down if ( ent->moverState == MOVER_POS2 ) { ent->nextthink = level.time + ent->wait; return; } // only partway down before reversing if ( ent->moverState == MOVER_2TO1 ) { total = ent->s.pos.trDuration; partial = level.time - ent->s.pos.trTime; if ( partial > total ) { partial = total; } MatchTeam( ent, MOVER_1TO2, level.time - ( total - partial ) ); if ( ent->sound1to2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound1to2 ); } return; } // only partway up before reversing if ( ent->moverState == MOVER_1TO2 ) { total = ent->s.pos.trDuration; partial = level.time - ent->s.pos.trTime; if ( partial > total ) { partial = total; } MatchTeam( ent, MOVER_2TO1, level.time - ( total - partial ) ); if ( ent->sound2to1 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound2to1 ); } return; } } /* ================ InitMover "pos1", "pos2", and "speed" should be set before calling, so the movement delta can be calculated ================ */ void InitMover( gentity_t *ent ) { vec3_t move; float distance; float light; vec3_t color; qboolean lightSet, colorSet; char *sound; // if the "model2" key is set, use a seperate model // for drawing, but clip against the brushes if ( ent->model2 ) { ent->s.modelindex2 = G_ModelIndex( ent->model2 ); } // if the "loopsound" key is set, use a constant looping sound when moving if ( G_SpawnString( "noise", "100", &sound ) ) { ent->s.loopSound = G_SoundIndex( sound ); } // if the "color" or "light" keys are set, setup constantLight lightSet = G_SpawnFloat( "light", "100", &light ); colorSet = G_SpawnVector( "color", "1 1 1", color ); if ( lightSet || colorSet ) { int r, g, b, i; r = color[0] * 255; if ( r > 255 ) { r = 255; } g = color[1] * 255; if ( g > 255 ) { g = 255; } b = color[2] * 255; if ( b > 255 ) { b = 255; } i = light / 4; if ( i > 255 ) { i = 255; } ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 ); } ent->use = Use_BinaryMover; ent->reached = Reached_BinaryMover; ent->moverState = MOVER_POS1; ent->r.svFlags = SVF_USE_CURRENT_ORIGIN; ent->s.eType = ET_MOVER; VectorCopy (ent->pos1, ent->r.currentOrigin); trap_LinkEntity (ent); ent->s.pos.trType = TR_STATIONARY; VectorCopy( ent->pos1, ent->s.pos.trBase ); // calculate time to reach second position from speed VectorSubtract( ent->pos2, ent->pos1, move ); distance = VectorLength( move ); if ( ! ent->speed ) { ent->speed = 100; } VectorScale( move, ent->speed, ent->s.pos.trDelta ); ent->s.pos.trDuration = distance * 1000 / ent->speed; if ( ent->s.pos.trDuration <= 0 ) { ent->s.pos.trDuration = 1; } } /* =============================================================================== DOOR A use can be triggered either by a touch function, by being shot, or by being targeted by another entity. =============================================================================== */ /* ================ Blocked_Door ================ */ void Blocked_Door( gentity_t *ent, gentity_t *other ) { // remove anything other than a client if ( !other->client ) { // except CTF flags!!!! if( other->s.eType == ET_ITEM && other->item->giType == IT_TEAM ) { Team_DroppedFlagThink( other ); return; } G_TempEntity( other->s.origin, EV_ITEM_POP ); G_FreeEntity( other ); return; } if ( ent->damage ) { if(g_awardpushing.integer) G_Damage( other, ent, ent->activator, NULL, NULL, ent->damage, 0, MOD_CRUSH ); else G_Damage( other, ent, ent, NULL, NULL, ent->damage, 0, MOD_CRUSH ); } if ( ent->spawnflags & 4 ) { return; // crushers don't reverse } // reverse direction Use_BinaryMover( ent, ent, other ); } /* ================ Touch_DoorTriggerSpectator ================ */ static void Touch_DoorTriggerSpectator( gentity_t *ent, gentity_t *other, trace_t *trace ) { int axis; float doorMin, doorMax; vec3_t origin; axis = ent->count; // the constants below relate to constants in Think_SpawnNewDoorTrigger() doorMin = ent->r.absmin[axis] + 100; doorMax = ent->r.absmax[axis] - 100; VectorCopy(other->client->ps.origin, origin); if (origin[axis] < doorMin || origin[axis] > doorMax) return; if (fabs(origin[axis] - doorMax) < fabs(origin[axis] - doorMin)) { origin[axis] = doorMin - 10; } else { origin[axis] = doorMax + 10; } TeleportPlayer(other, origin, tv(10000000.0, 0, 0)); } /* ================ Touch_DoorTrigger ================ */ void Touch_DoorTrigger( gentity_t *ent, gentity_t *other, trace_t *trace ) { if ( other->client && (other->client->sess.sessionTeam == TEAM_SPECTATOR || other->client->ps.pm_type == PM_SPECTATOR)) { // if the door is not open and not opening if ( ent->parent->moverState != MOVER_1TO2 && ent->parent->moverState != MOVER_POS2) { Touch_DoorTriggerSpectator( ent, other, trace ); } } else if ( ent->parent->moverState != MOVER_1TO2 ) { Use_BinaryMover( ent->parent, ent, other ); } } void Think_SpawnNewDoorTrigger( gentity_t *ent ) __attribute__((nonnull)); /* ====================== Think_SpawnNewDoorTrigger All of the parts of a door have been spawned, so create a trigger that encloses all of them ====================== */ void Think_SpawnNewDoorTrigger( gentity_t *ent ) { gentity_t *other; vec3_t mins, maxs; int i, best; // set all of the slaves as shootable for ( other = ent ; other ; other = other->teamchain ) { other->takedamage = qtrue; } // find the bounds of everything on the team VectorCopy (ent->r.absmin, mins); VectorCopy (ent->r.absmax, maxs); for (other = ent->teamchain ; other ; other=other->teamchain) { AddPointToBounds (other->r.absmin, mins, maxs); AddPointToBounds (other->r.absmax, mins, maxs); } // find the thinnest axis, which will be the one we expand best = 0; for ( i = 1 ; i < 3 ; i++ ) { if ( maxs[i] - mins[i] < maxs[best] - mins[best] ) { best = i; } } maxs[best] += 120; mins[best] -= 120; // create a trigger with this size other = G_Spawn (); other->classname = "door_trigger"; VectorCopy (mins, other->r.mins); VectorCopy (maxs, other->r.maxs); other->parent = ent; other->r.contents = CONTENTS_TRIGGER; other->touch = Touch_DoorTrigger; // remember the thinnest axis other->count = best; trap_LinkEntity (other); MatchTeam( ent, ent->moverState, level.time ); } void Think_MatchTeam( gentity_t *ent ) { MatchTeam( ent, ent->moverState, level.time ); } /*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER TOGGLE wait in both the start and end states for a trigger event. START_OPEN the door to moves to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). NOMONSTER monsters will not trigger this door "model2" .md3 model to also draw "angle" determines the opening direction "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "speed" movement speed (100 default) "wait" wait before returning (3 default, -1 = never return) "lip" lip remaining at end of move (8 default) "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius "health" if set, the door must be shot open */ void SP_func_door (gentity_t *ent) { vec3_t abs_movedir; float distance; vec3_t size; float lip; ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/doors/dr1_strt.wav"); ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/doors/dr1_end.wav"); ent->blocked = Blocked_Door; // default speed of 400 if (!ent->speed) ent->speed = 400; // default wait of 2 seconds if (!ent->wait) ent->wait = 2; ent->wait *= 1000; // default lip of 8 units G_SpawnFloat( "lip", "8", &lip ); // default damage of 2 points G_SpawnInt( "dmg", "2", &ent->damage ); // first position at start VectorCopy( ent->s.origin, ent->pos1 ); // calculate second position trap_SetBrushModel( ent, ent->model ); G_SetMovedir (ent->s.angles, ent->movedir); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); VectorSubtract( ent->r.maxs, ent->r.mins, size ); distance = DotProduct( abs_movedir, size ) - lip; VectorMA( ent->pos1, distance, ent->movedir, ent->pos2 ); // if "start_open", reverse position 1 and 2 if ( ent->spawnflags & 1 ) { vec3_t temp; VectorCopy( ent->pos2, temp ); VectorCopy( ent->s.origin, ent->pos2 ); VectorCopy( temp, ent->pos1 ); } InitMover( ent ); ent->nextthink = level.time + FRAMETIME; if ( ! (ent->flags & FL_TEAMSLAVE ) ) { int health; G_SpawnInt( "health", "0", &health ); if ( health ) { ent->takedamage = qtrue; } if ( ent->targetname || health ) { // non touch/shoot doors ent->think = Think_MatchTeam; } else { ent->think = Think_SpawnNewDoorTrigger; } } } /* =============================================================================== PLAT =============================================================================== */ /* ============== Touch_Plat Don't allow decent if a living player is on it =============== */ void Touch_Plat( gentity_t *ent, gentity_t *other, trace_t *trace ) { if ( !other->client || other->client->ps.stats[STAT_HEALTH] <= 0 ) { return; } // delay return-to-pos1 by one second if ( ent->moverState == MOVER_POS2 ) { ent->nextthink = level.time + 1000; } } /* ============== Touch_PlatCenterTrigger If the plat is at the bottom position, start it going up =============== */ void Touch_PlatCenterTrigger(gentity_t *ent, gentity_t *other, trace_t *trace ) { if ( !other->client ) { return; } if ( ent->parent->moverState == MOVER_POS1 ) { Use_BinaryMover( ent->parent, ent, other ); } } /* ================ SpawnPlatTrigger Spawn a trigger in the middle of the plat's low position Elevator cars require that the trigger extend through the entire low position, not just sit on top of it. ================ */ void SpawnPlatTrigger( gentity_t *ent ) { gentity_t *trigger; vec3_t tmin, tmax; // the middle trigger will be a thin trigger just // above the starting position trigger = G_Spawn(); trigger->classname = "plat_trigger"; trigger->touch = Touch_PlatCenterTrigger; trigger->r.contents = CONTENTS_TRIGGER; trigger->parent = ent; tmin[0] = ent->pos1[0] + ent->r.mins[0] + 33; tmin[1] = ent->pos1[1] + ent->r.mins[1] + 33; tmin[2] = ent->pos1[2] + ent->r.mins[2]; tmax[0] = ent->pos1[0] + ent->r.maxs[0] - 33; tmax[1] = ent->pos1[1] + ent->r.maxs[1] - 33; tmax[2] = ent->pos1[2] + ent->r.maxs[2] + 8; if ( tmax[0] <= tmin[0] ) { tmin[0] = ent->pos1[0] + (ent->r.mins[0] + ent->r.maxs[0]) *0.5; tmax[0] = tmin[0] + 1; } if ( tmax[1] <= tmin[1] ) { tmin[1] = ent->pos1[1] + (ent->r.mins[1] + ent->r.maxs[1]) *0.5; tmax[1] = tmin[1] + 1; } VectorCopy (tmin, trigger->r.mins); VectorCopy (tmax, trigger->r.maxs); trap_LinkEntity (trigger); } /*QUAKED func_plat (0 .5 .8) ? Plats are always drawn in the extended position so they will light correctly. "lip" default 8, protrusion above rest position "height" total height of movement, defaults to model height "speed" overrides default 200. "dmg" overrides default 2 "model2" .md3 model to also draw "color" constantLight color "light" constantLight radius */ void SP_func_plat (gentity_t *ent) { float lip, height; ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/plats/pt1_strt.wav"); ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/plats/pt1_end.wav"); VectorClear (ent->s.angles); G_SpawnFloat( "speed", "200", &ent->speed ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "wait", "1", &ent->wait ); G_SpawnFloat( "lip", "8", &lip ); ent->wait = 1000; // create second position trap_SetBrushModel( ent, ent->model ); if ( !G_SpawnFloat( "height", "0", &height ) ) { height = (ent->r.maxs[2] - ent->r.mins[2]) - lip; } // pos1 is the rest (bottom) position, pos2 is the top VectorCopy( ent->s.origin, ent->pos2 ); VectorCopy( ent->pos2, ent->pos1 ); ent->pos1[2] -= height; InitMover( ent ); // touch function keeps the plat from returning while // a live player is standing on it ent->touch = Touch_Plat; ent->blocked = Blocked_Door; ent->parent = ent; // so it can be treated as a door // spawn the trigger if one hasn't been custom made if ( !ent->targetname ) { SpawnPlatTrigger(ent); } } /* =============================================================================== BUTTON =============================================================================== */ /* ============== Touch_Button =============== */ void Touch_Button(gentity_t *ent, gentity_t *other, trace_t *trace ) { if ( !other->client ) { return; } if ( ent->moverState == MOVER_POS1 ) { Use_BinaryMover( ent, other, other ); } } /*QUAKED func_button (0 .5 .8) ? When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. "model2" .md3 model to also draw "angle" determines the opening direction "target" all entities with a matching targetname will be used "speed" override the default 40 speed "wait" override the default 1 second wait (-1 = never return) "lip" override the default 4 pixel lip remaining at end of move "health" if set, the button must be killed instead of touched "color" constantLight color "light" constantLight radius */ void SP_func_button( gentity_t *ent ) { vec3_t abs_movedir; float distance; vec3_t size; float lip; ent->sound1to2 = G_SoundIndex("sound/movers/switches/butn2.wav"); if ( !ent->speed ) { ent->speed = 40; } if ( !ent->wait ) { ent->wait = 1; } ent->wait *= 1000; // first position VectorCopy( ent->s.origin, ent->pos1 ); // calculate second position trap_SetBrushModel( ent, ent->model ); G_SpawnFloat( "lip", "4", &lip ); G_SetMovedir( ent->s.angles, ent->movedir ); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); VectorSubtract( ent->r.maxs, ent->r.mins, size ); distance = abs_movedir[0] * size[0] + abs_movedir[1] * size[1] + abs_movedir[2] * size[2] - lip; VectorMA (ent->pos1, distance, ent->movedir, ent->pos2); if (ent->health) { // shootable button ent->takedamage = qtrue; } else { // touchable button ent->touch = Touch_Button; } InitMover( ent ); } /* =============================================================================== TRAIN =============================================================================== */ #define TRAIN_START_ON 1 #define TRAIN_TOGGLE 2 #define TRAIN_BLOCK_STOPS 4 /* =============== Think_BeginMoving The wait time at a corner has completed, so start moving again =============== */ void Think_BeginMoving( gentity_t *ent ) { ent->s.pos.trTime = level.time; ent->s.pos.trType = TR_LINEAR_STOP; } /* =============== Reached_Train =============== */ void Reached_Train( gentity_t *ent ) { gentity_t *next; float speed; vec3_t move; float length; // copy the apropriate values next = ent->nextTrain; if ( !next || !next->nextTrain ) { return; // just stop } // fire all other targets G_UseTargets( next, NULL ); // set the new trajectory ent->nextTrain = next->nextTrain; VectorCopy( next->s.origin, ent->pos1 ); VectorCopy( next->nextTrain->s.origin, ent->pos2 ); // if the path_corner has a speed, use that if ( next->speed ) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.pos.trDuration = length * 1000 / speed; // Tequila comment: Be sure to send to clients after any fast move case ent->r.svFlags &= ~SVF_NOCLIENT; // Tequila comment: Fast move case if(ent->s.pos.trDuration<1) { // Tequila comment: As trDuration is used later in a division, we need to avoid that case now // With null trDuration, // the calculated rocks bounding box becomes infinite and the engine think for a short time // any entity is riding that mover but not the world entity... In rare case, I found it // can also stuck every map entities after func_door are used. // The desired effect with very very big speed is to have instant move, so any not null duration // lower than a frame duration should be sufficient. // Afaik, the negative case don't have to be supported. ent->s.pos.trDuration=1; // Tequila comment: Don't send entity to clients so it becomes really invisible ent->r.svFlags |= SVF_NOCLIENT; } // looping sound ent->s.loopSound = next->soundLoop; // start it going SetMoverState( ent, MOVER_1TO2, level.time ); // if there is a "wait" value on the target, don't start moving yet if ( next->wait ) { ent->nextthink = level.time + next->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } } /* =============== Think_SetupTrainTargets Link all the corners together =============== */ void Think_SetupTrainTargets( gentity_t *ent ) { gentity_t *path, *next, *start; ent->nextTrain = G_Find( NULL, FOFS(targetname), ent->target ); if ( !ent->nextTrain ) { G_Printf( "func_train at %s with an unfound target\n", vtos(ent->r.absmin) ); return; } start = NULL; for ( path = ent->nextTrain ; path != start ; path = next ) { if ( !start ) { start = path; } if ( !path->target ) { G_Printf( "Train corner at %s without a target\n", vtos(path->s.origin) ); return; } // find a path_corner among the targets // there may also be other targets that get fired when the corner // is reached next = NULL; do { next = G_Find( next, FOFS(targetname), path->target ); if ( !next ) { G_Printf( "Train corner at %s without a target path_corner\n", vtos(path->s.origin) ); return; } } while ( strcmp( next->classname, "path_corner" ) ); path->nextTrain = next; } // start the train moving from the first corner Reached_Train( ent ); } /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) Train path corners. Target: next path corner and other targets to fire "speed" speed to move to the next corner "wait" seconds to wait before behining move to next corner */ void SP_path_corner( gentity_t *self ) { if ( !self->targetname ) { G_Printf ("path_corner with no targetname at %s\n", vtos(self->s.origin)); G_FreeEntity( self ); return; } // path corners don't need to be linked in } /*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS A train is a mover that moves between path_corner target points. Trains MUST HAVE AN ORIGIN BRUSH. The train spawns at the first target it is pointing at. "model2" .md3 model to also draw "speed" default 100 "dmg" default 2 "noise" looping sound to play when the train is in motion "target" next path corner "color" constantLight color "light" constantLight radius */ void SP_func_train (gentity_t *self) { VectorClear (self->s.angles); if (self->spawnflags & TRAIN_BLOCK_STOPS) { self->damage = 0; } else { if (!self->damage) { self->damage = 2; } } if ( !self->speed ) { self->speed = 100; } if ( !self->target ) { G_Printf ("func_train without a target at %s\n", vtos(self->r.absmin)); G_FreeEntity( self ); return; } trap_SetBrushModel( self, self->model ); InitMover( self ); self->reached = Reached_Train; // start trains on the second frame, to make sure their targets have had // a chance to spawn self->nextthink = level.time + FRAMETIME; self->think = Think_SetupTrainTargets; } /* =============================================================================== STATIC =============================================================================== */ /*QUAKED func_static (0 .5 .8) ? A bmodel that just sits there, doing nothing. Can be used for conditional walls and models. "model2" .md3 model to also draw "color" constantLight color "light" constantLight radius */ void SP_func_static( gentity_t *ent ) { trap_SetBrushModel( ent, ent->model ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); } /* =============================================================================== ROTATING =============================================================================== */ /*QUAKED func_rotating (0 .5 .8) ? START_ON - X_AXIS Y_AXIS You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. "model2" .md3 model to also draw "speed" determines how fast it moves; default value is 100. "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_rotating (gentity_t *ent) { if ( !ent->speed ) { ent->speed = 100; } // set the axis of rotation ent->s.apos.trType = TR_LINEAR; if ( ent->spawnflags & 4 ) { ent->s.apos.trDelta[2] = ent->speed; } else if ( ent->spawnflags & 8 ) { ent->s.apos.trDelta[0] = ent->speed; } else { ent->s.apos.trDelta[1] = ent->speed; } if (!ent->damage) { ent->damage = 2; } trap_SetBrushModel( ent, ent->model ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy( ent->s.apos.trBase, ent->r.currentAngles ); trap_LinkEntity( ent ); } /* =============================================================================== BOBBING =============================================================================== */ /*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS Normally bobs on the Z axis "model2" .md3 model to also draw "height" amplitude of bob (32 default) "speed" seconds to complete a bob cycle (4 default) "phase" the 0.0 to 1.0 offset in the cycle to start at "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_bobbing (gentity_t *ent) { float height; float phase; G_SpawnFloat( "speed", "4", &ent->speed ); G_SpawnFloat( "height", "32", &height ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "phase", "0", &phase ); trap_SetBrushModel( ent, ent->model ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); ent->s.pos.trDuration = ent->speed * 1000; ent->s.pos.trTime = ent->s.pos.trDuration * phase; ent->s.pos.trType = TR_SINE; // set the axis of bobbing if ( ent->spawnflags & 1 ) { ent->s.pos.trDelta[0] = height; } else if ( ent->spawnflags & 2 ) { ent->s.pos.trDelta[1] = height; } else { ent->s.pos.trDelta[2] = height; } } /* =============================================================================== PENDULUM =============================================================================== */ /*QUAKED func_pendulum (0 .5 .8) ? You need to have an origin brush as part of this entity. Pendulums always swing north / south on unrotated models. Add an angles field to the model to allow rotation in other directions. Pendulum frequency is a physical constant based on the length of the beam and gravity. "model2" .md3 model to also draw "speed" the number of degrees each way the pendulum swings, (30 default) "phase" the 0.0 to 1.0 offset in the cycle to start at "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_pendulum(gentity_t *ent) { float freq; float length; float phase; float speed; G_SpawnFloat( "speed", "30", &speed ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "phase", "0", &phase ); trap_SetBrushModel( ent, ent->model ); // find pendulum length length = fabs( ent->r.mins[2] ); if ( length < 8 ) { length = 8; } freq = 1 / ( M_PI * 2 ) * sqrt( g_gravity.value*g_gravityModifier.value / ( 3 * length ) ); ent->s.pos.trDuration = ( 1000 / freq ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); ent->s.apos.trDuration = 1000 / freq; ent->s.apos.trTime = ent->s.apos.trDuration * phase; ent->s.apos.trType = TR_SINE; ent->s.apos.trDelta[2] = speed; } openarena_0.8.8.orig/code/game/ai_dmq3.h0000644000175000017500000002007711656310264016545 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmq3.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ //setup the deathmatch AI void BotSetupDeathmatchAI(void); //shutdown the deathmatch AI void BotShutdownDeathmatchAI(void); //let the bot live within it's deathmatch AI net void BotDeathmatchAI(bot_state_t *bs, float thinktime); //free waypoints void BotFreeWaypoints(bot_waypoint_t *wp); //choose a weapon void BotChooseWeapon(bot_state_t *bs); //setup movement stuff void BotSetupForMovement(bot_state_t *bs); //update the inventory void BotUpdateInventory(bot_state_t *bs); //update the inventory during battle void BotUpdateBattleInventory(bot_state_t *bs, int enemy); //use holdable items during battle void BotBattleUseItems(bot_state_t *bs); //return true if the bot is dead qboolean BotIsDead(bot_state_t *bs); //returns true if the bot is in observer mode qboolean BotIsObserver(bot_state_t *bs); //returns true if the bot is in the intermission qboolean BotIntermission(bot_state_t *bs); //returns true if the bot is in lava or slime qboolean BotInLavaOrSlime(bot_state_t *bs); //returns true if the entity is dead qboolean EntityIsDead(aas_entityinfo_t *entinfo); //returns true if the entity is invisible qboolean EntityIsInvisible(aas_entityinfo_t *entinfo); //returns true if the entity is shooting qboolean EntityIsShooting(aas_entityinfo_t *entinfo); //returns true if this entity has the kamikaze qboolean EntityHasKamikaze(aas_entityinfo_t *entinfo); // set a user info key/value pair void BotSetUserInfo(bot_state_t *bs, char *key, char *value); // set the team status (offense, defense etc.) void BotSetTeamStatus(bot_state_t *bs); //returns the name of the client char *ClientName(int client, char *name, int size); //returns an simplyfied client name char *EasyClientName(int client, char *name, int size); //returns the skin used by the client char *ClientSkin(int client, char *skin, int size); // returns the appropriate synonym context for the current game type and situation int BotSynonymContext(bot_state_t *bs); // set last ordered task int BotSetLastOrderedTask(bot_state_t *bs); // selection of goals for teamplay void BotTeamGoals(bot_state_t *bs, int retreat); //returns the aggression of the bot in the range [0, 100] float BotAggression(bot_state_t *bs); //returns how bad the bot feels float BotFeelingBad(bot_state_t *bs); //returns true if the bot wants to retreat int BotWantsToRetreat(bot_state_t *bs); //returns true if the bot wants to chase int BotWantsToChase(bot_state_t *bs); //returns true if the bot wants to help int BotWantsToHelp(bot_state_t *bs); //returns true if the bot can and wants to rocketjump int BotCanAndWantsToRocketJump(bot_state_t *bs); // returns true if the bot has a persistant powerup and a weapon int BotHasPersistantPowerupAndWeapon(bot_state_t *bs); //returns true if the bot wants to and goes camping int BotWantsToCamp(bot_state_t *bs); //the bot will perform attack movements bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl); //returns true if the bot and the entity are in the same team int BotSameTeam(bot_state_t *bs, int entnum); //returns true if teamplay is on int TeamPlayIsOn(void); // returns the client number of the team mate flag carrier (-1 if none) int BotTeamFlagCarrier(bot_state_t *bs); //returns visible team mate flag carrier if available int BotTeamFlagCarrierVisible(bot_state_t *bs); //returns visible enemy flag carrier if available int BotEnemyFlagCarrierVisible(bot_state_t *bs); //get the number of visible teammates and enemies void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range); //returns true if within the field of vision for the given angles qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles); //returns true and sets the .enemy field when an enemy is found int BotFindEnemy(bot_state_t *bs, int curenemy); //returns a roam goal void BotRoamGoal(bot_state_t *bs, vec3_t goal); //returns entity visibility in the range [0, 1] float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent); //the bot will aim at the current enemy void BotAimAtEnemy(bot_state_t *bs); //check if the bot should attack void BotCheckAttack(bot_state_t *bs); //AI when the bot is blocked void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate); //AI to predict obstacles int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal); //enable or disable the areas the blocking entity is in void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable); //pop an activate goal from the stack int BotPopFromActivateGoalStack(bot_state_t *bs); //clear the activate goal stack void BotClearActivateGoalStack(bot_state_t *bs); //returns the team the bot is in int BotTeam(bot_state_t *bs); //retuns the opposite team of the bot int BotOppositeTeam(bot_state_t *bs); //returns the flag the bot is carrying (CTFFLAG_?) int BotCTFCarryingFlag(bot_state_t *bs); //remember the last ordered task void BotRememberLastOrderedTask(bot_state_t *bs); //set ctf goals (defend base, get enemy flag) during seek void BotCTFSeekGoals(bot_state_t *bs); //set ctf goals (defend base, get enemy flag) during retreat void BotCTFRetreatGoals(bot_state_t *bs); // int Bot1FCTFCarryingFlag(bot_state_t *bs); int BotHarvesterCarryingCubes(bot_state_t *bs); void Bot1FCTFSeekGoals(bot_state_t *bs); void Bot1FCTFRetreatGoals(bot_state_t *bs); void BotObeliskSeekGoals(bot_state_t *bs); void BotObeliskRetreatGoals(bot_state_t *bs); void BotGoHarvest(bot_state_t *bs); void BotHarvesterSeekGoals(bot_state_t *bs); void BotHarvesterRetreatGoals(bot_state_t *bs); int BotTeamCubeCarrierVisible(bot_state_t *bs); int BotEnemyCubeCarrierVisible(bot_state_t *bs); //get a random alternate route goal towards the given base int BotGetAlternateRouteGoal(bot_state_t *bs, int base); //returns either the alternate route goal or the given goal bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal); //create a new waypoint bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum); //find a waypoint with the given name bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name); //strstr but case insensitive char *stristr(char *str, char *charset); //returns the number of the client with the given name int ClientFromName(char *name); int ClientOnSameTeamFromName(bot_state_t *bs, char *name); // int BotPointAreaNum(vec3_t origin); // void BotMapScripts(bot_state_t *bs); //ctf flags #define CTF_FLAG_NONE 0 #define CTF_FLAG_RED 1 #define CTF_FLAG_BLUE 2 //CTF skins #define CTF_SKIN_REDTEAM "red" #define CTF_SKIN_BLUETEAM "blue" extern int gametype; //game type extern int maxclients; //maximum number of clients extern vmCvar_t bot_grapple; extern vmCvar_t bot_rocketjump; extern vmCvar_t bot_fastchat; extern vmCvar_t bot_nochat; extern vmCvar_t bot_testrchat; extern vmCvar_t bot_challenge; extern bot_goal_t ctf_redflag; extern bot_goal_t ctf_blueflag; extern bot_goal_t ctf_neutralflag; extern bot_goal_t redobelisk; extern bot_goal_t blueobelisk; extern bot_goal_t neutralobelisk; openarena_0.8.8.orig/code/game/chars.h0000644000175000017500000001405011656310264016322 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //======================================================== //======================================================== //name #define CHARACTERISTIC_NAME 0 //string //gender of the bot #define CHARACTERISTIC_GENDER 1 //string ("male", "female", "it") //attack skill // > 0.0 && < 0.2 = don't move // > 0.3 && < 1.0 = aim at enemy during retreat // > 0.0 && < 0.4 = only move forward/backward // >= 0.4 && < 1.0 = circle strafing // > 0.7 && < 1.0 = random strafe direction change #define CHARACTERISTIC_ATTACK_SKILL 2 //float [0, 1] //weapon weight file #define CHARACTERISTIC_WEAPONWEIGHTS 3 //string //view angle difference to angle change factor #define CHARACTERISTIC_VIEW_FACTOR 4 //float <0, 1] //maximum view angle change #define CHARACTERISTIC_VIEW_MAXCHANGE 5 //float [1, 360] //reaction time in seconds #define CHARACTERISTIC_REACTIONTIME 6 //float [0, 5] //accuracy when aiming #define CHARACTERISTIC_AIM_ACCURACY 7 //float [0, 1] //weapon specific aim accuracy #define CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN 8 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_SHOTGUN 9 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER 10 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER 11 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_LIGHTNING 12 #define CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN 13 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_RAILGUN 14 #define CHARACTERISTIC_AIM_ACCURACY_BFG10K 15 //float [0, 1] //skill when aiming // > 0.0 && < 0.9 = aim is affected by enemy movement // > 0.4 && <= 0.8 = enemy linear leading // > 0.8 && <= 1.0 = enemy exact movement leading // > 0.5 && <= 1.0 = prediction shots when enemy is not visible // > 0.6 && <= 1.0 = splash damage by shooting nearby geometry #define CHARACTERISTIC_AIM_SKILL 16 //float [0, 1] //weapon specific aim skill #define CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER 17 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER 18 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_PLASMAGUN 19 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1] //======================================================== //chat //======================================================== //file with chats #define CHARACTERISTIC_CHAT_FILE 21 //string //name of the chat character #define CHARACTERISTIC_CHAT_NAME 22 //string //characters per minute type speed #define CHARACTERISTIC_CHAT_CPM 23 //integer [1, 4000] //tendency to insult/praise #define CHARACTERISTIC_CHAT_INSULT 24 //float [0, 1] //tendency to chat misc #define CHARACTERISTIC_CHAT_MISC 25 //float [0, 1] //tendency to chat at start or end of level #define CHARACTERISTIC_CHAT_STARTENDLEVEL 26 //float [0, 1] //tendency to chat entering or exiting the game #define CHARACTERISTIC_CHAT_ENTEREXITGAME 27 //float [0, 1] //tendency to chat when killed someone #define CHARACTERISTIC_CHAT_KILL 28 //float [0, 1] //tendency to chat when died #define CHARACTERISTIC_CHAT_DEATH 29 //float [0, 1] //tendency to chat when enemy suicides #define CHARACTERISTIC_CHAT_ENEMYSUICIDE 30 //float [0, 1] //tendency to chat when hit while talking #define CHARACTERISTIC_CHAT_HITTALKING 31 //float [0, 1] //tendency to chat when bot was hit but didn't dye #define CHARACTERISTIC_CHAT_HITNODEATH 32 //float [0, 1] //tendency to chat when bot hit the enemy but enemy didn't dye #define CHARACTERISTIC_CHAT_HITNOKILL 33 //float [0, 1] //tendency to randomly chat #define CHARACTERISTIC_CHAT_RANDOM 34 //float [0, 1] //tendency to reply #define CHARACTERISTIC_CHAT_REPLY 35 //float [0, 1] //======================================================== //movement //======================================================== //tendency to crouch #define CHARACTERISTIC_CROUCHER 36 //float [0, 1] //tendency to jump #define CHARACTERISTIC_JUMPER 37 //float [0, 1] //tendency to walk #define CHARACTERISTIC_WALKER 48 //float [0, 1] //tendency to jump using a weapon #define CHARACTERISTIC_WEAPONJUMPING 38 //float [0, 1] //tendency to use the grapple hook when available #define CHARACTERISTIC_GRAPPLE_USER 39 //float [0, 1] //use this!! //======================================================== //goal //======================================================== //item weight file #define CHARACTERISTIC_ITEMWEIGHTS 40 //string //the aggression of the bot #define CHARACTERISTIC_AGGRESSION 41 //float [0, 1] //the self preservation of the bot (rockets near walls etc.) #define CHARACTERISTIC_SELFPRESERVATION 42 //float [0, 1] //how likely the bot is to take revenge #define CHARACTERISTIC_VENGEFULNESS 43 //float [0, 1] //use this!! //tendency to camp #define CHARACTERISTIC_CAMPER 44 //float [0, 1] //======================================================== //======================================================== //tendency to get easy frags #define CHARACTERISTIC_EASY_FRAGGER 45 //float [0, 1] //how alert the bot is (view distance) #define CHARACTERISTIC_ALERTNESS 46 //float [0, 1] //how much the bot fires it's weapon #define CHARACTERISTIC_FIRETHROTTLE 47 //float [0, 1] openarena_0.8.8.orig/code/game/g_arenas.c0000644000175000017500000002615011656310264017000 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // g_arenas.c // #include "g_local.h" gentity_t *podium1; gentity_t *podium2; gentity_t *podium3; /* ================== UpdateTournamentInfo ================== */ void UpdateTournamentInfo( void ) { int i; gentity_t *player; int playerClientNum; int n, accuracy, perfect, msglen; #ifdef MISSIONPACK // bk001205 int score1, score2; qboolean won; #endif char buf[32]; char msg[MAX_STRING_CHARS]; // find the real player player = NULL; for (i = 0; i < level.maxclients; i++ ) { player = &g_entities[i]; if ( !player->inuse ) { continue; } if ( !( player->r.svFlags & SVF_BOT ) ) { break; } } // this should never happen! if ( !player || i == level.maxclients ) { return; } playerClientNum = i; CalculateRanks(); if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) { #ifdef MISSIONPACK Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum ); #else Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum ); #endif } else { if( player->client->accuracy_shots ) { accuracy = player->client->accuracy_hits * 100 / player->client->accuracy_shots; } else { accuracy = 0; } #ifdef MISSIONPACK won = qfalse; if (g_gametype.integer >= GT_CTF && g_ffa_gt==0) { score1 = level.teamScores[TEAM_RED]; score2 = level.teamScores[TEAM_BLUE]; if (level.clients[playerClientNum].sess.sessionTeam == TEAM_RED) { won = (level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]); } else { won = (level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED]); } } else { if (&level.clients[playerClientNum] == &level.clients[ level.sortedClients[0] ]) { won = qtrue; score1 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE]; score2 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE]; } else { score2 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE]; score1 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE]; } } if (won && player->client->ps.persistant[PERS_KILLED] == 0) { perfect = 1; } else { perfect = 0; } Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy, player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],player->client->ps.persistant[PERS_DEFEND_COUNT], player->client->ps.persistant[PERS_ASSIST_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE], perfect, score1, score2, level.time, player->client->ps.persistant[PERS_CAPTURES] ); #else perfect = ( level.clients[playerClientNum].ps.persistant[PERS_RANK] == 0 && player->client->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy, player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE], perfect ); #endif } msglen = strlen( msg ); for( i = 0; i < level.numNonSpectatorClients; i++ ) { n = level.sortedClients[i]; Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] ); msglen += strlen( buf ); if( msglen >= sizeof(msg) ) { break; } strcat( msg, buf ); } trap_SendConsoleCommand( EXEC_APPEND, msg ); } static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) { gentity_t *body; vec3_t vec; vec3_t f, r, u; body = G_Spawn(); if ( !body ) { G_Printf( S_COLOR_RED "ERROR: out of gentities\n" ); return NULL; } body->classname = ent->client->pers.netname; body->client = ent->client; body->s = ent->s; body->s.eType = ET_PLAYER; // could be ET_INVISIBLE body->s.eFlags = 0; // clear EF_TALK, etc body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce body->s.event = 0; body->s.pos.trType = TR_STATIONARY; body->s.groundEntityNum = ENTITYNUM_WORLD; body->s.legsAnim = LEGS_IDLE; body->s.torsoAnim = TORSO_STAND; if( body->s.weapon == WP_NONE ) { body->s.weapon = WP_MACHINEGUN; } if( body->s.weapon == WP_GAUNTLET) { body->s.torsoAnim = TORSO_STAND2; } body->s.event = 0; body->r.svFlags = ent->r.svFlags; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_BODY; body->r.ownerNum = ent->r.ownerNum; body->takedamage = qfalse; VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec ); vectoangles( vec, body->s.apos.trBase ); body->s.apos.trBase[PITCH] = 0; body->s.apos.trBase[ROLL] = 0; AngleVectors( body->s.apos.trBase, f, r, u ); VectorMA( pad->r.currentOrigin, offset[0], f, vec ); VectorMA( vec, offset[1], r, vec ); VectorMA( vec, offset[2], u, vec ); G_SetOrigin( body, vec ); trap_LinkEntity (body); body->count = place; return body; } static void CelebrateStop( gentity_t *player ) { int anim; if( player->s.weapon == WP_GAUNTLET) { anim = TORSO_STAND2; } else { anim = TORSO_STAND; } player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } #define TIMER_GESTURE (34*66+50) static void CelebrateStart( gentity_t *player ) { player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | TORSO_GESTURE; player->nextthink = level.time + TIMER_GESTURE; player->think = CelebrateStop; /* player->client->ps.events[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = EV_TAUNT; player->client->ps.eventParms[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = 0; player->client->ps.eventSequence++; */ G_AddEvent(player, EV_TAUNT, 0); } static vec3_t offsetFirst = {0, 0, 74}; static vec3_t offsetSecond = {-10, 60, 54}; static vec3_t offsetThird = {-19, -60, 45}; static void PodiumPlacementThink( gentity_t *podium ) { vec3_t vec; vec3_t origin; vec3_t f, r, u; podium->nextthink = level.time + 100; AngleVectors( level.intermission_angle, vec, NULL, NULL ); VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); G_SetOrigin( podium, origin ); if( podium1 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium1->s.apos.trBase ); podium1->s.apos.trBase[PITCH] = 0; podium1->s.apos.trBase[ROLL] = 0; AngleVectors( podium1->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetFirst[0], f, vec ); VectorMA( vec, offsetFirst[1], r, vec ); VectorMA( vec, offsetFirst[2], u, vec ); G_SetOrigin( podium1, vec ); } if( podium2 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium2->s.apos.trBase ); podium2->s.apos.trBase[PITCH] = 0; podium2->s.apos.trBase[ROLL] = 0; AngleVectors( podium2->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetSecond[0], f, vec ); VectorMA( vec, offsetSecond[1], r, vec ); VectorMA( vec, offsetSecond[2], u, vec ); G_SetOrigin( podium2, vec ); } if( podium3 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium3->s.apos.trBase ); podium3->s.apos.trBase[PITCH] = 0; podium3->s.apos.trBase[ROLL] = 0; AngleVectors( podium3->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetThird[0], f, vec ); VectorMA( vec, offsetThird[1], r, vec ); VectorMA( vec, offsetThird[2], u, vec ); G_SetOrigin( podium3, vec ); } } static gentity_t *SpawnPodium( void ) { gentity_t *podium; vec3_t vec; vec3_t origin; podium = G_Spawn(); if ( !podium ) { return NULL; } podium->classname = "podium"; podium->s.eType = ET_GENERAL; podium->s.number = podium - g_entities; podium->clipmask = CONTENTS_SOLID; podium->r.contents = CONTENTS_SOLID; podium->s.modelindex = G_ModelIndex( SP_PODIUM_MODEL ); AngleVectors( level.intermission_angle, vec, NULL, NULL ); VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); G_SetOrigin( podium, origin ); VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); podium->s.apos.trBase[YAW] = vectoyaw( vec ); trap_LinkEntity (podium); podium->think = PodiumPlacementThink; podium->nextthink = level.time + 100; return podium; } /* ================== SpawnModelsOnVictoryPads ================== */ void SpawnModelsOnVictoryPads( void ) { gentity_t *player; gentity_t *podium; podium1 = NULL; podium2 = NULL; podium3 = NULL; podium = SpawnPodium(); player = SpawnModelOnVictoryPad( podium, offsetFirst, &g_entities[level.sortedClients[0]], level.clients[ level.sortedClients[0] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { player->nextthink = level.time + 2000; player->think = CelebrateStart; podium1 = player; } player = SpawnModelOnVictoryPad( podium, offsetSecond, &g_entities[level.sortedClients[1]], level.clients[ level.sortedClients[1] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { podium2 = player; } if ( level.numNonSpectatorClients > 2 ) { player = SpawnModelOnVictoryPad( podium, offsetThird, &g_entities[level.sortedClients[2]], level.clients[ level.sortedClients[2] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { podium3 = player; } } } /* =============== Svcmd_AbortPodium_f =============== */ void Svcmd_AbortPodium_f( void ) { if( g_gametype.integer != GT_SINGLE_PLAYER ) { return; } if( podium1 ) { podium1->nextthink = level.time; podium1->think = CelebrateStop; } } openarena_0.8.8.orig/code/game/g_client.c0000644000175000017500000017470211711076326017014 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" // g_client.c -- client functions that don't happen every frame static vec3_t playerMins = {-15, -15, -24}; static vec3_t playerMaxs = {15, 15, 32}; /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial potential spawning position for deathmatch games. The first time a player enters the game, they will be at an 'initial' spot. Targets will be fired when someone spawns in on them. "nobots" will prevent bots from using this spot. "nohumans" will prevent non-bots from using this spot. */ void SP_info_player_deathmatch( gentity_t *ent ) { int i; G_SpawnInt( "nobots", "0", &i); if ( i ) { ent->flags |= FL_NO_BOTS; } G_SpawnInt( "nohumans", "0", &i ); if ( i ) { ent->flags |= FL_NO_HUMANS; } } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) equivelant to info_player_deathmatch */ void SP_info_player_start(gentity_t *ent) { ent->classname = "info_player_deathmatch"; SP_info_player_deathmatch( ent ); } //Three for Double_D void SP_info_player_dd(gentity_t *ent) { } void SP_info_player_dd_red(gentity_t *ent) { } void SP_info_player_dd_blue(gentity_t *ent) { } //One for Standard Domination, not really a player spawn point void SP_domination_point(gentity_t *ent) { } /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The intermission will be viewed from this point. Target an info_notnull for the view direction. */ void SP_info_player_intermission( gentity_t *ent ) { } /* ======================================================================= SelectSpawnPoint ======================================================================= */ /* ================ SpotWouldTelefrag ================ */ qboolean SpotWouldTelefrag( gentity_t *spot ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; vec3_t mins, maxs; VectorAdd( spot->s.origin, playerMins, mins ); VectorAdd( spot->s.origin, playerMaxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); for (i=0 ; iclient && hit->client->ps.stats[STAT_HEALTH] > 0 ) { if ( hit->client) { return qtrue; } } return qfalse; } /* ================ SelectNearestDeathmatchSpawnPoint Find the spot that we DON'T want to use ================ */ #define MAX_SPAWN_POINTS 128 gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from ) { gentity_t *spot; vec3_t delta; float dist, nearestDist; gentity_t *nearestSpot; nearestDist = 999999; nearestSpot = NULL; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { VectorSubtract( spot->s.origin, from, delta ); dist = VectorLength( delta ); if ( dist < nearestDist ) { nearestDist = dist; nearestSpot = spot; } } return nearestSpot; } /* ================ SelectRandomDeathmatchSpawnPoint go to a random point that doesn't telefrag ================ */ #define MAX_SPAWN_POINTS 128 gentity_t *SelectRandomDeathmatchSpawnPoint( void ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_SPAWN_POINTS]; count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } spots[ count ] = spot; count++; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), "info_player_deathmatch"); } selection = rand() % count; return spots[ selection ]; } /* =========== SelectRandomFurthestSpawnPoint Chooses a player start, deathmatch start, etc ============ */ gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { gentity_t *spot; vec3_t delta; float dist; float list_dist[64]; gentity_t *list_spot[64]; int numSpots, rnd, i, j; numSpots = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } VectorSubtract( spot->s.origin, avoidPoint, delta ); dist = VectorLength( delta ); for (i = 0; i < numSpots; i++) { if ( dist > list_dist[i] ) { if ( numSpots >= 64 ) numSpots = 64-1; for (j = numSpots; j > i; j--) { list_dist[j] = list_dist[j-1]; list_spot[j] = list_spot[j-1]; } list_dist[i] = dist; list_spot[i] = spot; numSpots++; if (numSpots > 64) numSpots = 64; break; } } if (i >= numSpots && numSpots < 64) { list_dist[numSpots] = dist; list_spot[numSpots] = spot; numSpots++; } } if (!numSpots) { spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch"); if (!spot) G_Error( "Couldn't find a spawn point" ); VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } // select a random spot from the spawn points furthest away rnd = random() * (numSpots / 2); VectorCopy (list_spot[rnd]->s.origin, origin); origin[2] += 9; VectorCopy (list_spot[rnd]->s.angles, angles); return list_spot[rnd]; } /* =========== SelectSpawnPoint Chooses a player start, deathmatch start, etc ============ */ gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { //return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles ); gentity_t *spot; gentity_t *nearestSpot; nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint ); spot = SelectRandomDeathmatchSpawnPoint ( ); if ( spot == nearestSpot ) { // roll again if it would be real close to point of death spot = SelectRandomDeathmatchSpawnPoint ( ); if ( spot == nearestSpot ) { // last try spot = SelectRandomDeathmatchSpawnPoint ( ); } } // find a single player start spot if (!spot) { G_Error( "Couldn't find a spawn point" ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } /* =========== SelectInitialSpawnPoint Try to find a spawn point marked 'initial', otherwise use normal spawn selection. ============ */ gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( spot->spawnflags & 1 ) { break; } } if ( !spot || SpotWouldTelefrag( spot ) ) { return SelectSpawnPoint( vec3_origin, origin, angles ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } /* =========== SelectSpectatorSpawnPoint ============ */ gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles ) { //gentity_t *spot; FindIntermissionPoint(); VectorCopy( level.intermission_origin, origin ); VectorCopy( level.intermission_angle, angles ); //for some reason we need to return an specific point in elimination (this might not be neccecary anymore but to be sure...) //if(g_gametype.integer == GT_ELIMINATION) // return SelectSpawnPoint( vec3_origin, origin, angles ); //VectorCopy (origin,spot->s.origin); //spot->s.origin[2] += 9; //VectorCopy (angles, spot->s.angles); return NULL; //spot; } /* ======================================================================= BODYQUE ======================================================================= */ /* =============== InitBodyQue =============== */ void InitBodyQue (void) { int i; gentity_t *ent; level.bodyQueIndex = 0; for (i=0; iclassname = "bodyque"; ent->neverFree = qtrue; level.bodyQue[i] = ent; } } /* ============= BodySink After sitting around for five seconds, fall into the ground and dissapear ============= */ void BodySink( gentity_t *ent ) { if ( level.time - ent->timestamp > 6500 ) { // the body ques are never actually freed, they are just unlinked trap_UnlinkEntity( ent ); ent->physicsObject = qfalse; return; } ent->nextthink = level.time + 100; ent->s.pos.trBase[2] -= 1; } /* ============= CopyToBodyQue A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ void CopyToBodyQue( gentity_t *ent ) { gentity_t *e; int i; gentity_t *body; int contents; trap_UnlinkEntity (ent); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( ent->s.origin, -1 ); if ( (contents & CONTENTS_NODROP) && !(ent->s.eFlags & EF_KAMIKAZE) ) { //the check for kamikaze is a workaround for ctf4ish return; } // grab a body que and cycle to the next one body = level.bodyQue[ level.bodyQueIndex ]; level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; //Check if the next body has the kamikaze, in that case skip it. for(i=0;(level.bodyQue[ level.bodyQueIndex ]->s.eFlags & EF_KAMIKAZE) && (i<10);i++) { level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; } body->s = ent->s; body->s.eFlags = EF_DEAD; // clear EF_TALK, etc if ( ent->s.eFlags & EF_KAMIKAZE ) { ent->s.eFlags &= ~EF_KAMIKAZE; body->s.eFlags |= EF_KAMIKAZE; // check if there is a kamikaze timer around for this owner for (i = 0; i < MAX_GENTITIES; i++) { e = &g_entities[i]; if (!e->inuse) continue; if (e->activator != ent) continue; if (strcmp(e->classname, "kamikaze timer")) continue; e->activator = body; break; } } body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce if ( body->s.groundEntityNum == ENTITYNUM_NONE ) { body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); } else { body->s.pos.trType = TR_STATIONARY; } body->s.event = 0; // change the animation to the last-frame only, so the sequence // doesn't repeat anew for the body switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } body->r.svFlags = ent->r.svFlags; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_CORPSE; body->r.ownerNum = ent->s.number; body->nextthink = level.time + 5000; body->think = BodySink; body->die = body_die; // don't take more damage if already gibbed if ( ent->health <= GIB_HEALTH ) { body->takedamage = qfalse; } else { body->takedamage = qtrue; } VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity (body); } //====================================================================== /* ================== SetClientViewAngle ================== */ void SetClientViewAngle( gentity_t *ent, vec3_t angle ) { int i; // set the delta angle for (i=0 ; i<3 ; i++) { int cmdAngle; cmdAngle = ANGLE2SHORT(angle[i]); ent->client->ps.delta_angles[i] = cmdAngle - ent->client->pers.cmd.angles[i]; } VectorCopy( angle, ent->s.angles ); VectorCopy (ent->s.angles, ent->client->ps.viewangles); } /* ================ respawn ================ */ void ClientRespawn( gentity_t *ent ) { gentity_t *tent; if((g_gametype.integer!=GT_ELIMINATION && g_gametype.integer!=GT_CTF_ELIMINATION && g_gametype.integer !=GT_LMS) && !ent->client->isEliminated) { ent->client->isEliminated = qtrue; //must not be true in warmup //Tried moving CopyToBodyQue } else { //Must always be false in other gametypes ent->client->isEliminated = qfalse; } CopyToBodyQue (ent); //Unlinks ent if(g_gametype.integer==GT_LMS) { if(ent->client->pers.livesLeft>0) { //ent->client->pers.livesLeft--; Coutned down somewhere else ent->client->isEliminated = qfalse; } else //We have used all our lives { if( ent->client->isEliminated!=qtrue) { ent->client->isEliminated = qtrue; if((g_lms_mode.integer == 2 || g_lms_mode.integer == 3) && level.roundNumber == level.roundNumberStarted) LMSpoint(); //Sago: This is really bad //TODO: Try not to make people spectators here ent->client->sess.spectatorState = PM_SPECTATOR; //We have to force spawn imidiantly to prevent lag. ClientSpawn(ent); } return; } } if((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION || g_gametype.integer==GT_LMS) && ent->client->ps.pm_type == PM_SPECTATOR && ent->client->ps.stats[STAT_HEALTH] > 0) return; ClientSpawn(ent); // add a teleportation effect if(g_gametype.integer!=GT_ELIMINATION && g_gametype.integer!=GT_CTF_ELIMINATION && g_gametype.integer!=GT_LMS) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; } } /* ================ respawnRound ================ */ void respawnRound( gentity_t *ent ) { gentity_t *tent; //if(g_gametype.integer!=GT_ELIMINATION || !ent->client->isEliminated) //{ // ent->client->isEliminated = qtrue; //CopyToBodyQue (ent); //} //if(g_gametype.integer==GT_ELIMINATION && ent->client->ps.pm_type == PM_SPECTATOR && ent->client->ps.stats[STAT_HEALTH] > 0) // return; if(ent->client->hook) Weapon_HookFree(ent->client->hook); trap_UnlinkEntity (ent); ClientSpawn(ent); // add a teleportation effect if(g_gametype.integer!=GT_ELIMINATION && g_gametype.integer!=GT_CTF_ELIMINATION && g_gametype.integer!=GT_LMS) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; } } /* ================ TeamCvarSet Sets the red and blue team client number cvars. ================ */ void TeamCvarSet( void ) { int i; qboolean redChanged = qfalse; qboolean blueChanged = qfalse; qboolean first = qtrue; char* temp = NULL; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].sess.sessionTeam == TEAM_RED ) { if(first) { temp = va("%i",i); first = qfalse; } else temp = va("%s,%i",temp,i); } } if(Q_stricmp(g_redTeamClientNumbers.string,temp)) redChanged = qtrue; trap_Cvar_Set("g_redTeamClientNumbers",temp); //Set it right first= qtrue; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].sess.sessionTeam == TEAM_BLUE ) { if(first) { temp = va("%i",i); first = qfalse; } else temp = va("%s,%i",temp,i); } } if(Q_stricmp(g_blueTeamClientNumbers.string,temp)) blueChanged = qtrue; trap_Cvar_Set("g_blueTeamClientNumbers",temp); //Note: We need to force update of the cvar or SendYourTeamMessage will send the old cvar value! if(redChanged) { trap_Cvar_Update(&g_redTeamClientNumbers); //Force update of CVAR SendYourTeamMessageToTeam(TEAM_RED); } if(blueChanged) { trap_Cvar_Update(&g_blueTeamClientNumbers); SendYourTeamMessageToTeam(TEAM_BLUE); //Force update of CVAR } } /* ================ TeamCount Returns number of players on a team ================ */ team_t TeamCount( int ignoreClientNum, int team ) { int i; int count = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( i == ignoreClientNum ) { continue; } if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].pers.connected == CON_CONNECTING) { continue; } if ( level.clients[i].sess.sessionTeam == team ) { count++; } } return count; } /* ================ TeamLivingCount Returns number of living players on a team ================ */ team_t TeamLivingCount( int ignoreClientNum, int team ) { int i; int count = 0; qboolean LMS = (g_gametype.integer==GT_LMS); for ( i = 0 ; i < level.maxclients ; i++ ) { if ( i == ignoreClientNum ) { continue; } if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].pers.connected == CON_CONNECTING) { continue; } //crash if g_gametype.integer is used here, why? if ( level.clients[i].sess.sessionTeam == team && (level.clients[i].ps.stats[STAT_HEALTH]>0 || LMS) && !(level.clients[i].isEliminated)) { count++; } } return count; } /* ================ TeamHealthCount Count total number of healthpoints on teh teams used for draws in Elimination ================ */ team_t TeamHealthCount(int ignoreClientNum, int team ) { int i; int count = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( i == ignoreClientNum ) { continue; } if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].pers.connected == CON_CONNECTING) { continue; } //only count clients with positive health if ( level.clients[i].sess.sessionTeam == team && (level.clients[i].ps.stats[STAT_HEALTH]>0)&& !(level.clients[i].isEliminated)) { count+=level.clients[i].ps.stats[STAT_HEALTH]; } } return count; } /* ================ RespawnAll Forces all clients to respawn. ================ */ void RespawnAll(void) { int i; gentity_t *client; for(i=0;iclient->ps.pm_type = PM_NORMAL; client->client->pers.livesLeft = g_lms_lives.integer; respawnRound(client); } return; } /* ================ RespawnDead Forces all *DEAD* clients to respawn. ================ */ void RespawnDead(void) { int i; gentity_t *client; for(i=0;iclient->pers.livesLeft = g_lms_lives.integer-1; if ( level.clients[i].isEliminated == qfalse ){ continue; } if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR ) { continue; } client->client->pers.livesLeft = g_lms_lives.integer; respawnRound(client); } return; } /* ================ DisableWeapons disables all weapons ================ */ void DisableWeapons(void) { int i; gentity_t *client; for(i=0;iclient->ps.pm_flags |= PMF_ELIMWARMUP; } ProximityMine_RemoveAll(); //Remove all the prox mines return; } /* ================ EnableWeapons enables all weapons ================ */ void EnableWeapons(void) { int i; gentity_t *client; for(i=0;iclient->ps.pm_flags &= ~PMF_ELIMWARMUP; } return; } /* ================ LMSpoint Gives a point to the lucky survivor ================ */ void LMSpoint(void) { int i; gentity_t *client; for(i=0;ihealth <= 0 ){ continue; } */ client->client->ps.persistant[PERS_SCORE] += 1; G_LogPrintf("PlayerScore: %i %i: %s now has %d points\n", i, client->client->ps.persistant[PERS_SCORE], client->client->pers.netname, client->client->ps.persistant[PERS_SCORE] ); } CalculateRanks(); return; } /* ================ TeamLeader Returns the client number of the team leader ================ */ int TeamLeader( int team ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].sess.sessionTeam == team ) { if ( level.clients[i].sess.teamLeader ) return i; } } return -1; } /* ================ PickTeam ================ */ team_t PickTeam( int ignoreClientNum ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ignoreClientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ignoreClientNum, TEAM_RED ); //KK-OAX Both Teams locked...forget about it, print an error message, keep as spec if ( level.RedTeamLocked && level.BlueTeamLocked ) { G_Printf( "Both teams have been locked by the Admin! \n" ); return TEAM_NONE; } if ( ( counts[TEAM_BLUE] > counts[TEAM_RED] ) && ( !level.RedTeamLocked ) ) { return TEAM_RED; } if ( ( counts[TEAM_RED] > counts[TEAM_BLUE] ) && ( !level.BlueTeamLocked ) ) { return TEAM_BLUE; } // equal team count, so join the team with the lowest score if ( ( level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED] ) && ( !level.RedTeamLocked ) ) { return TEAM_RED; } if ( ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) && ( !level.BlueTeamLocked ) ) { return TEAM_BLUE; } //KK-OAX Force Team Blue? return TEAM_BLUE; } /* =========== ForceClientSkin Forces a client's skin (for teamplay) =========== */ /* static void ForceClientSkin( gclient_t *client, char *model, const char *skin ) { char *p; if ((p = strrchr(model, '/')) != 0) { *p = 0; } Q_strcat(model, MAX_QPATH, "/"); Q_strcat(model, MAX_QPATH, skin); } */ /* =========== ClientCheckName ============ */ static void ClientCleanName(const char *in, char *out, int outSize, int clientNum) { int outpos = 0, colorlessLen = 0, spaces = 0, notblack=0; qboolean black = qfalse; // discard leading spaces for(; *in == ' '; in++); for(; *in && outpos < outSize - 1; in++) { out[outpos] = *in; if(*in == ' ') { // don't allow too many consecutive spaces if(spaces > 2) continue; spaces++; } else if(outpos > 0 && out[outpos - 1] == Q_COLOR_ESCAPE) { if(Q_IsColorString(&out[outpos - 1])) { colorlessLen--; if(ColorIndex(*in) == 0) { // Disallow color black in names to prevent players // from getting advantage playing in front of black backgrounds //outpos--; //continue; black = qtrue; } else black = qfalse; } else { spaces = 0; colorlessLen++; } } else { spaces = 0; colorlessLen++; if(!black && (Q_isalpha(*in) || (*in>='0' && *in<='9') ) ) notblack++; } outpos++; } out[outpos] = '\0'; //There was none not-black alphanum chars. Remove all colors if(notblack<1) Q_CleanStr(out); // don't allow empty names if( *out == '\0' || colorlessLen == 0) Q_strncpyz(out, va("Nameless%i",clientNum), outSize ); } /* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, team, health; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; //KK-OAX char err[MAX_STRING_CHARS]; qboolean revertName = qfalse; gclient_t *client; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char redTeam[MAX_INFO_STRING]; char blueTeam[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if ( !Info_Validate(userinfo) ) { strcpy (userinfo, "\\name\\badinfo"); } // check for local client s = Info_ValueForKey( userinfo, "ip" ); if ( !strcmp( s, "localhost" ) ) { client->pers.localClient = qtrue; } // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { client->pers.predictItemPickup = qfalse; } else { client->pers.predictItemPickup = qtrue; } //unlagged - client options // see if the player has opted out s = Info_ValueForKey( userinfo, "cg_delag" ); if ( !atoi( s ) ) { client->pers.delag = 0; } else { client->pers.delag = atoi( s ); } // see if the player is nudging his shots s = Info_ValueForKey( userinfo, "cg_cmdTimeNudge" ); client->pers.cmdTimeNudge = atoi( s ); // see if the player wants to debug the backward reconciliation /*s = Info_ValueForKey( userinfo, "cg_debugDelag" ); if ( !atoi( s ) ) { client->pers.debugDelag = qfalse; } else { client->pers.debugDelag = qtrue; }*/ // see if the player is simulating incoming latency //s = Info_ValueForKey( userinfo, "cg_latentSnaps" ); //client->pers.latentSnaps = atoi( s ); // see if the player is simulating outgoing latency //s = Info_ValueForKey( userinfo, "cg_latentCmds" ); //client->pers.latentCmds = atoi( s ); // see if the player is simulating outgoing packet loss //s = Info_ValueForKey( userinfo, "cg_plOut" ); //client->pers.plOut = atoi( s ); //unlagged - client options // set name Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname), clientNum ); //KK-OAPub Added From Tremulous-Control Name Changes if( strcmp( oldname, client->pers.netname ) ) { if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( client->pers.muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( !G_admin_name_check( ent, client->pers.netname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } //Never revert a bots name... just to bad if it hapens... but the bot will always be expendeble :-) if (ent->r.svFlags & SVF_BOT) revertName = qfalse; if( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } // N_G: this condition makes no sense to me and I'm not going to // try finding out what it means, I've added parentheses according to // evaluation rules of the original code so grab a // parentheses pairing highlighting text editor and see for yourself // if you got it right //Sago: One redundant check and CTF Elim and LMS was missing. Not an important function and I might never have noticed, should properly be || if ( ( ( client->sess.sessionTeam == TEAM_SPECTATOR ) || ( ( ( client->isEliminated ) /*|| ( client->ps.pm_type == PM_SPECTATOR )*/ ) && //Sago: If this is true client.isEliminated or TEAM_SPECTATOR is true to and this is redundant ( g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) ) ) && ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) ) { Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) ); } if ( client->pers.connected == CON_CONNECTED ) { if ( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname) ); } } // set max health if (client->ps.powerups[PW_GUARD]) { client->pers.maxHealth = 200; } else { health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } } client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM && g_ffa_gt==0) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } // bots set their team a few frames later if (g_gametype.integer >= GT_TEAM && g_ffa_gt==0 && g_entities[clientNum].r.svFlags & SVF_BOT) { s = Info_ValueForKey( userinfo, "team" ); if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } client->sess.sessionTeam = team; } else { team = client->sess.sessionTeam; } /* NOTE: all client side now Sago: I am not happy with this exception // team switch( team ) { case TEAM_RED: ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); break; case TEAM_BLUE: ForceClientSkin(client, model, "blue"); // ForceClientSkin(client, headModel, "blue"); break; } // don't ever use a default skin in teamplay, it would just waste memory // however bots will always join a team but they spawn in as spectator if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); } */ if (g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { client->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } } /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { client->pers.pmoveFixed = qfalse; } else { client->pers.pmoveFixed = qtrue; } */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors if( g_gametype.integer >= GT_TEAM && g_ffa_gt==0 && g_instantgib.integer) { switch(team) { case TEAM_RED: c1[0] = COLOR_BLUE; c2[0] = COLOR_BLUE; c1[1] = 0; c2[1] = 0; break; case TEAM_BLUE: c1[0] = COLOR_RED; c2[0] = COLOR_RED; c1[1] = 0; c2[1] = 0; break; default: break; } } else { strcpy(c1, Info_ValueForKey( userinfo, "color1" )); strcpy(c2, Info_ValueForKey( userinfo, "color2" )); } strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if ( ent->r.svFlags & SVF_BOT ) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+clientNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\\id\\%s\n", clientNum, s, Info_ValueForKey(userinfo, "cl_guid") ); } /* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = {""}; int i; //KK-OAX I moved these up so userinfo could be assigned/used. ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; ent->client = client; memset( client, 0, sizeof(*client) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) ); // IP filtering //KK-OAX Has this been obsoleted? // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); Q_strncpyz( client->pers.ip, value, sizeof( client->pers.ip ) ); if ( G_FilterPacket( value ) && !Q_stricmp(value,"localhost") ) { G_Printf("Player with IP: %s is banned\n",value); return "You are banned from this server."; } if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } //KK-OAX // we don't check GUID or password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if ( !isBot && (strcmp(value, "localhost") != 0)) { // check for a password value = Info_ValueForKey (userinfo, "password"); if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value) != 0) { return "Invalid password"; } for( i = 0; i < sizeof( client->pers.guid ) - 1 && isxdigit( client->pers.guid[ i ] ); i++ ); if( i < sizeof( client->pers.guid ) - 1 ) return "Invalid GUID"; for( i = 0; i < level.maxclients; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } } //Check for local client if( !strcmp( client->pers.ip, "localhost" ) ) client->pers.localClient = qtrue; client->pers.adminLevel = G_admin_level( ent ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( clientNum, !firstTime ) ) { return "BotConnectfailed"; } } //KK-OAX Swapped these in order...seemed to help the connection process. // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); G_LogPrintf( "ClientConnect: %i\n", clientNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) ); } if ( g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR ) { BroadcastTeamChange( client, -1 ); } // count current clients and rank for scoreboard CalculateRanks(); // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); //Sago: Changed the message //unlagged - backward reconciliation #5 // announce it if ( g_delagHitscan.integer ) { trap_SendServerCommand( clientNum, "print \"Full lag compensation is ON!\n\"" ); } else { trap_SendServerCommand( clientNum, "print \"Full lag compensation is OFF!\n\"" ); } //unlagged - backward reconciliation #5 G_admin_namelog_update( client, qfalse ); return NULL; } void motd (gentity_t *ent) { char motd[1024]; fileHandle_t motdFile; int motdLen; int fileLen; strcpy (motd, "cp \""); fileLen = trap_FS_FOpenFile(g_motdfile.string, &motdFile, FS_READ); if(motdFile) { char * p; motdLen = strlen(motd); if((motdLen + fileLen) > (sizeof(motd) - 2)) fileLen = (sizeof(motd) - 2 - motdLen); trap_FS_Read(motd + motdLen, fileLen, motdFile); motd[motdLen + fileLen] = '"'; motd[motdLen + fileLen + 1] = 0; trap_FS_FCloseFile(motdFile); while((p = strchr(motd, '\r'))) //Remove carrier return. 0x0D memmove(p, p + 1, motdLen + fileLen - (p - motd)); } trap_SendServerCommand(ent - g_entities, motd); } /* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the level. This will happen every level load, and on transition between teams, but doesn't happen on respawns ============ */ void ClientBegin( int clientNum ) { gentity_t *ent; gclient_t *client; gentity_t *tent; int flags; int countRed, countBlue, countFree; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); ent = g_entities + clientNum; client = level.clients + clientNum; if ( ent->r.linked ) { trap_UnlinkEntity( ent ); } G_InitGentity( ent ); ent->touch = 0; ent->pain = 0; ent->client = client; client->pers.connected = CON_CONNECTED; client->pers.enterTime = level.time; client->pers.teamState.state = TEAM_BEGIN; //Elimination: client->pers.roundReached = 0; //We will spawn in next round if(g_gametype.integer == GT_LMS) { client->isEliminated = qtrue; //So player does not give a point in gamemode 2 and 3 //trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " will start dead\n\"", client->pers.netname) ); } //player is a bot: if( ent->r.svFlags & SVF_BOT ) { if(!level.hadBots) { G_LogPrintf( "Info: There has been at least 1 bot now\n" ); level.hadBots = qtrue; } } //Count smallest team countFree = TeamCount(-1,TEAM_FREE); countRed = TeamCount(-1,TEAM_RED); countBlue = TeamCount(-1,TEAM_BLUE); if(g_gametype.integer < GT_TEAM || g_ffa_gt) { if(countFree>level.teamSize) level.teamSize=countFree; } else if(countRed>countBlue) { if(countBlue>level.teamSize) level.teamSize=countBlue; } else { if(countRed>level.teamSize) level.teamSize=countRed; } // save eflags around this, because changing teams will // cause this to happen with a valid entity, and we // want to make sure the teleport bit is set right // so the viewpoint doesn't interpolate through the // world to the new position flags = client->ps.eFlags; memset( &client->ps, 0, sizeof( client->ps ) ); if( client->sess.sessionTeam != TEAM_SPECTATOR ) PlayerStore_restore(Info_ValueForKey(userinfo,"cl_guid"),&(client->ps)); client->ps.eFlags = flags; // locate ent at a spawn point ClientSpawn( ent ); if( ( client->sess.sessionTeam != TEAM_SPECTATOR ) && ( ( !( client->isEliminated ) /*&& ( ( !client->ps.pm_type ) == PM_SPECTATOR ) */ ) || //Sago: Yes, it made no sense ( ( g_gametype.integer != GT_ELIMINATION || level.intermissiontime) && ( g_gametype.integer != GT_CTF_ELIMINATION || level.intermissiontime) && ( g_gametype.integer != GT_LMS || level.intermissiontime ) ) ) ) { // send event tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; if ( g_gametype.integer != GT_TOURNAMENT ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) ); } } motd ( ent ); G_LogPrintf( "ClientBegin: %i\n", clientNum ); //Send domination point names: if(g_gametype.integer == GT_DOMINATION) { DominationPointNamesMessage(ent); DominationPointStatusMessage(ent); } TeamCvarSet(); // count current clients and rank for scoreboard CalculateRanks(); //Send the list of custom vote options: if(strlen(custom_vote_info)) SendCustomVoteCommands(clientNum); } /* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; //gentity_t *tent; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots,vote; int accuracy[WP_NUM_WEAPONS][2]; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; //In Elimination the player should not spawn if he have already spawned in the round (but not for spectators) // N_G: You've obviously wanted something ELSE //Sago: Yes, the !level.intermissiontime is currently redundant but it might still be the bast place to make the test, CheckElimination in g_main makes sure the next if will fail and the rest of the things this block does might not affect if in Intermission (I'll just test that) if( ( ( g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || (g_gametype.integer == GT_LMS && client->isEliminated)) && (!level.intermissiontime || level.warmupTime != 0) ) && ( client->sess.sessionTeam != TEAM_SPECTATOR ) ) { // N_G: Another condition that makes no sense to me, see for // yourself if you really meant this // Sago: I beleive the TeamCount is to make sure people can join even if the game can't start if( ( level.roundNumber == level.roundNumberStarted ) || ( (level.time < level.roundStartTime - g_elimination_activewarmup.integer*1000 ) && TeamCount( -1, TEAM_BLUE ) && TeamCount( -1, TEAM_RED ) ) ) { client->sess.spectatorState = SPECTATOR_FREE; client->isEliminated = qtrue; if(g_gametype.integer == GT_LMS) G_LogPrintf( "LMS: %i %i %i: Player \"%s^7\" eliminated!\n", level.roundNumber, index, 1, client->pers.netname ); client->ps.pm_type = PM_SPECTATOR; CalculateRanks(); return; } else { client->pers.roundReached = level.roundNumber+1; client->sess.spectatorState = SPECTATOR_NOT; client->ps.pm_type = PM_NORMAL; client->isEliminated = qfalse; CalculateRanks(); } } else { //Force false. if(client->isEliminated) { client->isEliminated = qfalse; CalculateRanks(); } } if(g_gametype.integer == GT_LMS && client->sess.sessionTeam != TEAM_SPECTATOR && (!level.intermissiontime || level.warmupTime != 0)) { if(level.roundNumber==level.roundNumberStarted /*|| level.timeclient->pers.livesLeft) { client->sess.spectatorState = SPECTATOR_FREE; if( ent->client->isEliminated!=qtrue) { client->isEliminated = qtrue; if((g_lms_mode.integer == 2 || g_lms_mode.integer == 3) && level.roundNumber == level.roundNumberStarted) LMSpoint(); G_LogPrintf( "LMS: %i %i %i: Player \"%s^7\" eliminated!\n", level.roundNumber, index, 1, client->pers.netname ); } client->ps.pm_type = PM_SPECTATOR; return; } client->sess.spectatorState = SPECTATOR_NOT; client->ps.pm_type = PM_NORMAL; client->isEliminated = qfalse; if(client->pers.livesLeft>0) client->pers.livesLeft--; } // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ((client->sess.sessionTeam == TEAM_SPECTATOR) || ( (client->ps.pm_type == PM_SPECTATOR || client->isEliminated ) && (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) ) ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_DOUBLE_D) { //Double Domination uses special spawn points: spawnPoint = SelectDoubleDominationSpawnPoint (client->sess.sessionTeam, spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF && g_ffa_gt==0 && g_gametype.integer!= GT_DOMINATION) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { //Sago: The game has a tendency to select the furtest spawn point //This is a problem if the fursest spawnpoint keeps being NO_BOTS and it does! //This is a hot fix that seeks a spawn point faraway from the the currently found one vec3_t old_origin; VectorCopy(spawn_origin,old_origin); spawnPoint = SelectSpawnPoint (old_origin, spawn_origin, spawn_angles); if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; //unlagged - backward reconciliation #3 // we don't want players being backward-reconciled to the place they died G_ResetHistory( ent ); // and this is as good a time as any to clear the saved state ent->client->saved.leveltime = 0; //unlagged - backward reconciliation #3 // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; vote = client->vote; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; memcpy(accuracy,client->accuracy,sizeof(accuracy)); memcpy(persistant,client->ps.persistant,MAX_PERSISTANT*sizeof(int)); eventSequence = client->ps.eventSequence; Com_Memset (client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->vote = vote; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; for( i = 0 ; i < WP_NUM_WEAPONS ; i++ ){ client->accuracy[i][0] = accuracy[i][0]; client->accuracy[i][1] = accuracy[i][1]; } client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; //Sago: No one has hit the client yet! client->lastSentFlying = -1; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; if(g_gametype.integer != GT_ELIMINATION && g_gametype.integer != GT_CTF_ELIMINATION && g_gametype.integer != GT_LMS && !g_elimination_allgametypes.integer) { client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN ); if ( g_gametype.integer == GT_TEAM ) { client->ps.ammo[WP_MACHINEGUN] = 50; } else { client->ps.ammo[WP_MACHINEGUN] = 100; } client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; client->ps.ammo[WP_GRAPPLING_HOOK] = -1; // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; } else { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; client->ps.ammo[WP_GRAPPLING_HOOK] = -1; if (g_elimination_machinegun.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_MACHINEGUN ); client->ps.ammo[WP_MACHINEGUN] = g_elimination_machinegun.integer; } if (g_elimination_shotgun.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SHOTGUN ); client->ps.ammo[WP_SHOTGUN] = g_elimination_shotgun.integer; } if (g_elimination_grenade.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GRENADE_LAUNCHER ); client->ps.ammo[WP_GRENADE_LAUNCHER] = g_elimination_grenade.integer; } if (g_elimination_rocket.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ROCKET_LAUNCHER ); client->ps.ammo[WP_ROCKET_LAUNCHER] = g_elimination_rocket.integer; } if (g_elimination_lightning.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_LIGHTNING ); client->ps.ammo[WP_LIGHTNING] = g_elimination_lightning.integer; } if (g_elimination_railgun.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_RAILGUN ); client->ps.ammo[WP_RAILGUN] = g_elimination_railgun.integer; } if (g_elimination_plasmagun.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_PLASMAGUN ); client->ps.ammo[WP_PLASMAGUN] = g_elimination_plasmagun.integer; } if (g_elimination_bfg.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BFG ); client->ps.ammo[WP_BFG] = g_elimination_bfg.integer; } if (g_elimination_grapple.integer) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GRAPPLING_HOOK ); } if (g_elimination_nail.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_NAILGUN ); client->ps.ammo[WP_NAILGUN] = g_elimination_nail.integer; } if (g_elimination_mine.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_PROX_LAUNCHER ); client->ps.ammo[WP_PROX_LAUNCHER] = g_elimination_mine.integer; } if (g_elimination_chain.integer > 0) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_CHAINGUN ); client->ps.ammo[WP_CHAINGUN] = g_elimination_chain.integer; } ent->health = client->ps.stats[STAT_ARMOR] = g_elimination_startArmor.integer; //client->ps.stats[STAT_MAX_HEALTH]*2; ent->health = client->ps.stats[STAT_HEALTH] = g_elimination_startHealth.integer; //client->ps.stats[STAT_MAX_HEALTH]*2; // ent->health = client->ps.stats[STAT_HEALTH] = 0; } //Instantgib mode, replace weapons with rail (and maybe gauntlet) if(g_instantgib.integer) { client->ps.stats[STAT_WEAPONS] = ( 1 << WP_RAILGUN ); client->ps.ammo[WP_RAILGUN] = 999; //Don't display any ammo if(g_instantgib.integer>1) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; } } //nexuiz style rocket arena (rocket launcher only) if(g_rockets.integer) { client->ps.stats[STAT_WEAPONS] = ( 1 << WP_ROCKET_LAUNCHER ); client->ps.ammo[WP_ROCKET_LAUNCHER] = 999; } G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; if(g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION || g_gametype.integer==GT_LMS) client->ps.pm_flags |= PMF_ELIMWARMUP; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) || ((client->ps.pm_type == PM_SPECTATOR || client->isEliminated) && (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) ) ) { //Sago: Lets see if this fixes the bots only bug - loose all point on dead bug. (It didn't) /*if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) { G_KillBox( ent ); trap_LinkEntity (ent); }*/ } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = WP_MACHINEGUN; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) && i !=WP_GRAPPLING_HOOK ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( (ent->client->sess.sessionTeam != TEAM_SPECTATOR) || ( (!client->isEliminated || client->ps.pm_type != PM_SPECTATOR)&& (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) ) ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); if(g_spawnprotect.integer) client->spawnprotected = qtrue; RespawnTimeMessage(ent,0); } /* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; int i; char userinfo[MAX_INFO_STRING]; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } ClientLeaving( clientNum); //KK-OAX Admin G_admin_namelog_update( ent->client, qtrue ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( (level.clients[i].sess.sessionTeam == TEAM_SPECTATOR || level.clients[i].ps.pm_type == PM_SPECTATOR) && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected /* *Sago: I have removed this. A little dangerous but I make him suicide in a moment. */ /*if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); TossClientPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( ent ); } //#endif }*/ //Is the player alive? i = (ent->client->ps.stats[STAT_HEALTH]>0); //Commit suicide! if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR && i ) { //Prevent a team from loosing point because of player leaving int teamscore = 0; if(g_gametype.integer == GT_TEAM) teamscore = level.teamScores[ ent->client->sess.sessionTeam ]; // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, g_entities + ENTITYNUM_WORLD, 100000, MOD_SUICIDE); if(g_gametype.integer == GT_TEAM) level.teamScores[ ent->client->sess.sessionTeam ] = teamscore; } if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR) PlayerStore_store(Info_ValueForKey(userinfo,"cl_guid"),ent->client->ps); G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedClients[1] == clientNum ) { level.clients[ level.sortedClients[0] ].sess.wins++; ClientUserinfoChanged( level.sortedClients[0] ); } if( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE && level.intermissiontime ) { trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; level.changemap = NULL; level.intermissiontime = 0; } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); CountVotes(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } } openarena_0.8.8.orig/code/game/g_killspree.h0000644000175000017500000000477011656310264017532 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Karl Kuglin This file is part of the Open Arena source code. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //definitions // I'd say 32 sprees is a lot!!! #define MAX_KSPREE 32 #define MAX_DSPREE 32 #define MAX_MULTIKILLS 32 #define CENTER_PRINT 1 #define CHAT 2 //Used to pass parameter to calculate the upper-most level of // the spree/multikill arrays. #define LARGEST_KILLSPREE 1 #define LARGEST_DEATHSPREE 2 #define LARGEST_MULTIKILL 3 typedef struct killspree { int spreeLevel; int streakCount; //Added programatically based off SpreeDivisor. char spreeMsg[ MAX_STRING_CHARS ]; char sound2Play[ MAX_STRING_CHARS ]; int position; } killspree_t; typedef struct deathspree { int spreeLevel; int streakCount; //Added programatically based off SpreeDivisor. char spreeMsg[ MAX_STRING_CHARS ]; char sound2Play[ MAX_STRING_CHARS ]; int position; } deathspree_t; typedef struct multikill { char killMsg[ MAX_STRING_CHARS ]; char sound2Play[ MAX_STRING_CHARS ]; int kills; } multikill_t; //function declarations qboolean G_ReadAltKillSettings( gentity_t *ent, int skiparg ); void G_RunStreakLogic( gentity_t *attacker, gentity_t *victim ); void G_CheckForSpree( gentity_t *ent, int streak2Test, qboolean checkKillSpree ); void G_checkForMultiKill( gentity_t *ent ); void G_ConfigClientExcellent( qboolean levelStart ); openarena_0.8.8.orig/code/game/g_killspree.c0000644000175000017500000004622311656310264017524 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2009 Karl Kuglin This file is part of the Open Arena source code. Parts of this file utilize code originally written for Tremulous by Tony J. White. Use of that code is governed by GPL version 2 and any later versions. Open Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // NOTE: This code is by no means complete. #include "g_local.h" killspree_t *killSprees[ MAX_KSPREE ]; deathspree_t *deathSprees[ MAX_DSPREE ]; multikill_t *multiKills[ MAX_MULTIKILLS ]; /* ================= G_ReadAltKillSettings Since this is cvar dependent, it has to be placed in G_InitGame after cvars are registered. ================= */ qboolean G_ReadAltKillSettings( gentity_t *ent, int skiparg ) { //Let's Initialize some Spree structs/objects killspree_t *k = NULL; deathspree_t *d = NULL; multikill_t *m = NULL; //spree counters int ksc = 0, dsc = 0, mc = 0; int spreeDivisor; //Give us an int to use in "for" loops int i = 0; //File Stuff fileHandle_t file; int length; qboolean kspree_read; qboolean dspree_read; qboolean mkill_read; char *cnf, *cnf2; char *t; //Let's clear out any existing killing sprees/death sprees. YAYY BG_FREE!!!!! for( i = 0; i < MAX_KSPREE && killSprees[ i ]; i++ ) { BG_Free( killSprees[ i ] ); killSprees[ i ] = NULL; } for( i = 0; i < MAX_KSPREE && deathSprees[ i ]; i++ ) { BG_Free( deathSprees[ i ] ); deathSprees[ i ] = NULL; } for( i = 0; i < MAX_MULTIKILLS && multiKills[ i ]; i++ ) { BG_Free( multiKills[ i ] ); multiKills[ i ] = NULL; } // If the config file is not defined...forget reading/loading if( !g_sprees.string[0] ) { //Let's disable multikills to keep stock excellent sound if( g_altExcellent.integer == 1 ) { trap_Cvar_Set( "g_altExcellent", "0" ); } return qfalse; } /* only set spreeDivisor to cvar g_spreeDiv if g_spreeDiv is >= "2". 0 will cause problems and having 1 is just a fool who wants to hear something on every kill. */ if( g_spreeDiv.integer >= 2 ) { level.spreeDivisor = g_spreeDiv.integer; spreeDivisor = level.spreeDivisor; } else { level.spreeDivisor = 5; // We don't want to change the value, keep reminding the server operator. //g_spreeDiv.integer = 5; spreeDivisor = 5; G_Printf( "Error: cvar g_spreeDiv must not be set to 0 or 1, reverting to default settings!\n" ); G_Printf( "Error: Set g_spreeDiv higher than 1 if 5 is not desired!\n" ); } length = trap_FS_FOpenFile( g_sprees.string, &file, FS_READ ); //If the file can't be accessed/opened. if( length < 0 ) { G_Printf( "Could not open configuration file for Sprees and Multikills %s\n", g_sprees.string ); trap_Cvar_Set( "g_altExcellent", "0" ); return qfalse; } //Allocate some memory. cnf = BG_Alloc( length + 1 ); cnf2 = cnf; //Load the whole file up. trap_FS_Read( cnf, length, file ); *( cnf + length ) = '\0'; trap_FS_FCloseFile( file ); kspree_read = dspree_read = mkill_read = qfalse; //Let's start parsing em. COM_BeginParseSession( g_sprees.string ); while( 1 ) { t = COM_Parse( &cnf ); if( !*t ) break; if( !Q_stricmp( t, "[kspree]" ) ) { if( ksc >= MAX_KSPREE ) return qfalse; k = BG_Alloc( sizeof( killspree_t ) ); killSprees[ ksc++ ] = k; kspree_read = qtrue; dspree_read = qfalse; mkill_read = qfalse; } else if ( !Q_stricmp( t, "[dspree]" ) ) { if( dsc >= MAX_DSPREE ) return qfalse; d = BG_Alloc( sizeof( deathspree_t ) ); deathSprees[ dsc++ ] = d; dspree_read = qtrue; kspree_read = qfalse; mkill_read = qfalse; } else if ( !Q_stricmp( t, "[mkill]" ) ) { if( mc >= MAX_MULTIKILLS ) return qfalse; m = BG_Alloc( sizeof( multikill_t ) ); multiKills[ mc++ ] = m; mkill_read = qtrue; kspree_read = qfalse; dspree_read = qfalse; //Parse a killing spree } else if ( kspree_read ) { if( !Q_stricmp( t, "level" ) ) { readFile_int( &cnf, &k->spreeLevel ); //Let's take the spreeLevel and multiply it by the spreeDivisor to give us our count k->streakCount = ( ( k->spreeLevel ) * ( spreeDivisor ) ); } else if ( !Q_stricmp( t, "message" ) ) { readFile_string( &cnf, k->spreeMsg, sizeof( k->spreeMsg ) ); } else if ( !Q_stricmp( t, "printpos" ) ) { readFile_int( &cnf, &k->position ); } else if ( !Q_stricmp( t, "sound" ) ) { readFile_string( &cnf, k->sound2Play, sizeof( k->sound2Play ) ); } else { COM_ParseError( "Killing Spree unrecognized token \"%s\"", t ); } } else if ( dspree_read ) { if( !Q_stricmp( t, "level" ) ) { readFile_int( &cnf, &d->spreeLevel ); //Let's take the spreeLevel and multiply it by the spreeDivisor to give us our count d->streakCount = ( ( d->spreeLevel ) * ( spreeDivisor ) ); } else if ( !Q_stricmp( t, "message" ) ) { readFile_string( &cnf, d->spreeMsg, sizeof( d->spreeMsg ) ); } else if ( !Q_stricmp( t, "printpos" ) ) { readFile_int( &cnf, &d->position ); } else if ( !Q_stricmp( t, "sound" ) ) { readFile_string( &cnf, d->sound2Play, sizeof( d->sound2Play ) ); } else { COM_ParseError( "Death Spree unrecognized token \"%s\"", t ); } } else if ( mkill_read ) { if ( !Q_stricmp( t, "kills" ) ) { readFile_int( &cnf, &m->kills ); } else if ( !Q_stricmp( t, "message" ) ) { readFile_string( &cnf, m->killMsg, sizeof( m->killMsg ) ); } else if ( !Q_stricmp( t, "sound" ) ) { readFile_string( &cnf, m->sound2Play, sizeof( m->sound2Play ) ); } else { COM_ParseError( "Multikill unrecognized token \"%s\"", t ); } } else { COM_ParseError( "unexpected token \"%s\"", t ); } } //Let's "free" some memory now. BG_Free( cnf2 ); G_Printf("Sprees/Kills: loaded %d killing sprees, %d death sprees, and %d multikills.\n", ksc, dsc, mc ); //Mark the Upper Bounds of the Arrays (Since they start numbering at 0, We subtract 1 ) level.kSpreeUBound = ( ksc - 1 ); level.dSpreeUBound = ( dsc - 1 ); if( mc > 0 ) { level.mKillUBound = ( mc - 1 ); } else { level.mKillUBound = -1; //KK-OAX We don't have any kills defined, revert to stock. //FIXME: Make sure this change shows up in the console... if( g_altExcellent.integer == 1 ) { trap_Cvar_Set( "g_altExcellent", "0" ); } } return qtrue; } static char *fillPlaceHolder( char *stringToSearch, char *placeHolder, char *replaceWith ) { static char output[ MAX_SAY_TEXT ]; char *p; if( !( p = strstr( stringToSearch, placeHolder ) ) ) return stringToSearch; strncpy( output, stringToSearch, p - stringToSearch ); output[ p - stringToSearch ] = '\0'; Q_snprintf( output + ( p - stringToSearch ), output - stringToSearch, "%s%s", replaceWith, p + strlen( placeHolder ) ); return output; } //This concatenate's the message to broadcast to the clients. static char *CreateMessage( gentity_t *ent, char *message, char *spreeNumber ) { static char output[ MAX_SAY_TEXT ] = { "" }; char name[ MAX_NAME_LENGTH ]; //Do some sanity checks if( !ent ) { return output; } else if ( !*message ) { return output; } else if ( !spreeNumber ) { return output; } //Get the player name. Q_strncpyz( name, ent->client->pers.netname, sizeof( name ) ); //Do Our Replacements Q_strncpyz( output, fillPlaceHolder( message, "[n]", name ), sizeof( output ) ); Q_strncpyz( output, fillPlaceHolder( output, "[k]", spreeNumber ), sizeof( output ) ); return output; } /* ================ G_RunStreakLogic KK-OAX This is called from player_die. It does all the adding resetting of kill/death streaks to be compared against the kill/death spree levels. ================ */ void G_RunStreakLogic( gentity_t *attacker, gentity_t *victim ) { //We only want to sanity check for the victim at this point. if( !victim || !victim->client ) return; //We will reset the victim's killstreak counter victim->client->pers.killstreak = 0; //Add one to the death streak counter victim->client->pers.deathstreak++; //Let's check for a deathspree for the victim G_CheckForSpree( victim, victim->client->pers.deathstreak, qfalse ); //Move on to the attacker //Make sure they are a client and that the attacker and victim are not the same client //We don't want suicide to count towards a killstreak... if( ( attacker ) && ( attacker->client ) && ( attacker != victim ) ) { //Check the gametype--If FFA enabled, everybody's on the same team. if( g_gametype.integer >= GT_TEAM && g_ffa_gt!= 1 ) { //If they are on the same team we don't want to count it towards a killing spree. if( OnSameTeam( victim, attacker ) ) { return; } } //Add to the killstreak, reset the deathstreak attacker->client->pers.deathstreak = 0; attacker->client->pers.killstreak++; //Let's check for a killingspree for the attacker G_CheckForSpree( attacker, attacker->client->pers.killstreak, qtrue ); } } //If the streak / spree divisor is a whole number, we have a spree static qboolean TestSpreeWhole( int streak2Test ) { float float2Test; float spreeFDiv; float resultf; int spreeDiv; int result; float2Test = streak2Test; spreeFDiv = level.spreeDivisor; spreeDiv = level.spreeDivisor; result = ( streak2Test / spreeDiv ); resultf = ( float2Test / spreeFDiv ); if( result == resultf ) { return qtrue; } else { return qfalse; } } /* ================== G_CheckForSpree ================== */ void G_CheckForSpree( gentity_t *ent, int streak2Test, qboolean checkKillSpree ) { int i; char *returnedString; //If somebody want's to award killing sprees above 99 kills he/she can mod this his/herself!!! :) char streakcount[ 3 ]; char *sound; int position; int soundIndex; qboolean isSpree = qfalse; int divisionHolder; //Probably Not Needed, but to protect Server Ops from Crashing their Stuff MidMatch if( level.spreeDivisor < 1 ) { return; } divisionHolder = ( streak2Test / level.spreeDivisor ); //if it's a deathspree if( !checkKillSpree ) { //Is the streak higher than the largest level defined? if( divisionHolder > level.dSpreeUBound ) { //Let's make sure it's a whole number to mimic the other sprees isSpree = TestSpreeWhole( streak2Test ); if( !isSpree ) { return; } //We've made it this far...now do the largest spree defined. Q_snprintf( streakcount, sizeof( streakcount ), "%i", streak2Test ); //Check if deathSprees is NULL (actual problem!) if(!deathSprees[ level.dSpreeUBound ]) return; returnedString = CreateMessage( ent, deathSprees[ level.dSpreeUBound ]->spreeMsg, streakcount ); position = deathSprees[ level.dSpreeUBound ]->position; sound = deathSprees[ level.dSpreeUBound ]->sound2Play; soundIndex = G_SoundIndex( sound ); //Play the Sound G_GlobalSound( soundIndex ); //Print the Message if( position == CENTER_PRINT ) { AP( va("cp \"%s\"", returnedString ) ); } else { AP( va("chat \"%s\"", returnedString ) ); } } else { for( i = 0; deathSprees[ i ]; i++ ) { //If the deathSpree is equal to the streak to test if( deathSprees[ i ]->streakCount == streak2Test ) { //Using Q_snprintf to change the int into a char for replacement. Q_snprintf( streakcount, sizeof( streakcount ), "%i", deathSprees[ i ]->streakCount ); //Let's grab the message to show, fill up the placeholders and concatenate it. returnedString = CreateMessage ( ent, deathSprees[ i ]->spreeMsg, streakcount ); //Grab the Print Position ( 1 for Center Printing, 2 for Chat ) position = deathSprees[ i ]->position; //Grab the Sound sound = deathSprees[ i ]->sound2Play; //Index the Sound soundIndex = G_SoundIndex( sound ); //Play the Sound G_GlobalSound( soundIndex ); //Print the Message if( position == CENTER_PRINT ) { AP( va("cp \"%s\"", returnedString ) ); } else { AP( va("chat \"%s\"", returnedString ) ); } break; } } } } else /*if( checkKillSpree )*/ { //Is the streak higher than the largest level defined? if( divisionHolder > level.kSpreeUBound ) { //Let's make sure it's a whole number to mimic the other sprees isSpree = TestSpreeWhole( streak2Test ); if( !isSpree ) { return; } //We've made it this far...now do the largest spree defined. Q_snprintf( streakcount, sizeof( streakcount ), "%i", streak2Test ); //Check if killSprees is NULL (actual problem!) if(!killSprees[ level.kSpreeUBound ]) return; returnedString = CreateMessage( ent, killSprees[ level.kSpreeUBound ]->spreeMsg, streakcount ); position = killSprees[ level.kSpreeUBound ]->position; sound = killSprees[ level.kSpreeUBound ]->sound2Play; soundIndex = G_SoundIndex( sound ); soundIndex = G_SoundIndex( sound ); //G_GlobalSound( soundIndex ); G_Sound(ent,0,soundIndex); /* Doesn't do anything at the moment. cp does not work while kill message is displayed * if( position == CENTER_PRINT ) { //Only Center print for player doing the killing spree CP( va("cp \"%s\"", returnedString ) ); }*/ AP( va("chat \"%s\"", returnedString ) ); } else { for( i = 0; killSprees[ i ]; i++ ) { if( killSprees[ i ]->streakCount == streak2Test ) { Q_snprintf( streakcount, sizeof( streakcount ), "%i", killSprees[ i ]->streakCount ); returnedString = CreateMessage ( ent, killSprees[ i ]->spreeMsg, streakcount ); position = killSprees[ i ]->position; sound = killSprees[ i ]->sound2Play; soundIndex = G_SoundIndex( sound ); soundIndex = G_SoundIndex( sound ); //G_GlobalSound( soundIndex ); G_Sound(ent,0,soundIndex); /*if( position == CENTER_PRINT ) { //Only Center print for player doing the killing spree CP( va("cp \"%s\"", returnedString ) ); }*/ AP( va("chat \"%s\"", returnedString ) ); break; } } } } /*else { G_Printf("Killing Spree Error in G_CheckForSpree\n"); return; }*/ } /* =============== G_checkForMultiKill =============== */ void G_checkForMultiKill( gentity_t *ent ) { int i; char *returnedString; char *sound; int soundIndex; int multiKillCount; char multiKillString[ 2 ]; gclient_t *client; int clientNum; client = ent->client; clientNum = client - level.clients; //Let's grab the multikill count for the player first multiKillCount = ent->client->pers.multiKillCount; if( multiKillCount > multiKills[ level.mKillUBound ]->kills ) { Q_snprintf( multiKillString, sizeof( multiKillString ), "%i", multiKillCount ); if(!multiKills[ level.mKillUBound ]) return; //If null returnedString = CreateMessage ( ent, multiKills[ level.mKillUBound ]->killMsg, multiKillString ); sound = multiKills[ level.mKillUBound ]->sound2Play; soundIndex = G_SoundIndex( sound ); G_Sound(ent, 0, soundIndex ); AP( va("chat \"%s\"", returnedString ) ); return; } else { for( i = 0; multiKills[ i ]; i++ ) { //If the multikill count is equal to a killLevel let's do this. if( multiKills[ i ]->kills == multiKillCount ) { Q_snprintf( multiKillString, sizeof( multiKillString ), "%i", multiKills[ i ]->kills ); //Build the Message returnedString = CreateMessage ( ent, multiKills[ i ]->killMsg, multiKillString ); //Grab the sound sound = multiKills[ i ]->sound2Play; //Index the sound soundIndex = G_SoundIndex( sound ); //Play the sound G_Sound(ent, 0, soundIndex ); /* Print the String Since we don't want to clutter screens (the player is already going to get the excellent icon) we won't give them an option to centerprint. */ AP( va("chat \"%s\"", returnedString ) ); break; } } } } openarena_0.8.8.orig/code/game/g_local.h0000644000175000017500000014135111656310264016627 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copyPl of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_local.h -- local definitions for game module #include "../qcommon/q_shared.h" #include "bg_public.h" #include "g_public.h" #include "challenges.h" //================================================================== // the "gameversion" client command will print this plus compile date #define GAMEVERSION BASEGAME #define BODY_QUEUE_SIZE 8 #define INFINITE 1000000 #define FRAMETIME 100 // msec #define CARNAGE_REWARD_TIME 3000 #define REWARD_SPRITE_TIME 2000 #define INTERMISSION_DELAY_TIME 1000 #define SP_INTERMISSION_DELAY_TIME 5000 //Domination how many seconds between awarded a point (multiplied by two if more than 3 points) #define DOM_SECSPERPOINT 2000 //limit of the votemaps.cfg file and other custom map files #define MAX_MAPS_TEXT 8192 // gentity->flags #define FL_GODMODE 0x00000010 #define FL_NOTARGET 0x00000020 #define FL_TEAMSLAVE 0x00000400 // not the first on the team #define FL_NO_KNOCKBACK 0x00000800 #define FL_DROPPED_ITEM 0x00001000 #define FL_NO_BOTS 0x00002000 // spawn point not for bot use #define FL_NO_HUMANS 0x00004000 // spawn point just for bots #define FL_FORCE_GESTURE 0x00008000 // force gesture on client // movers are things like doors, plats, buttons, etc typedef enum { MOVER_POS1, MOVER_POS2, MOVER_1TO2, MOVER_2TO1 } moverState_t; #define SP_PODIUM_MODEL "models/mapobjects/podium/podium4.md3" //============================================================================ typedef struct gentity_s gentity_t; typedef struct gclient_s gclient_t; struct gentity_s { entityState_t s; // communicated by server to clients entityShared_t r; // shared by both the server system and game // DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER // EXPECTS THE FIELDS IN THAT ORDER! //================================ struct gclient_s *client; // NULL if not a client qboolean inuse; char *classname; // set in QuakeEd int spawnflags; // set in QuakeEd qboolean neverFree; // if true, FreeEntity will only unlink // bodyque uses this int flags; // FL_* variables char *model; char *model2; int freetime; // level.time when the object was freed int eventTime; // events will be cleared EVENT_VALID_MSEC after set qboolean freeAfterEvent; qboolean unlinkAfterEvent; qboolean physicsObject; // if true, it can be pushed by movers and fall off edges // all game items are physicsObjects, float physicsBounce; // 1.0 = continuous bounce, 0.0 = no bounce int clipmask; // brushes with this content value will be collided against // when moving. items and corpses do not collide against // players, for instance // movers moverState_t moverState; int soundPos1; int sound1to2; int sound2to1; int soundPos2; int soundLoop; gentity_t *parent; gentity_t *nextTrain; gentity_t *prevTrain; vec3_t pos1, pos2; char *message; int timestamp; // body queue sinking, etc float angle; // set in editor, -1 = up, -2 = down char *target; char *targetname; char *team; char *targetShaderName; char *targetShaderNewName; gentity_t *target_ent; float speed; vec3_t movedir; int nextthink; void (*think)(gentity_t *self); void (*reached)(gentity_t *self); // movers call this when hitting endpoint void (*blocked)(gentity_t *self, gentity_t *other); void (*touch)(gentity_t *self, gentity_t *other, trace_t *trace); void (*use)(gentity_t *self, gentity_t *other, gentity_t *activator); void (*pain)(gentity_t *self, gentity_t *attacker, int damage); void (*die)(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod); int pain_debounce_time; int fly_sound_debounce_time; // wind tunnel int last_move_time; int health; qboolean takedamage; int damage; int splashDamage; // quad will increase this without increasing radius int splashRadius; int methodOfDeath; int splashMethodOfDeath; int count; gentity_t *chain; gentity_t *enemy; gentity_t *activator; gentity_t *teamchain; // next entity in team gentity_t *teammaster; // master of the team int kamikazeTime; int kamikazeShockTime; int watertype; int waterlevel; int noise_index; // timing variables float wait; float random; gitem_t *item; // for bonus items }; typedef enum { CON_DISCONNECTED, CON_CONNECTING, CON_CONNECTED } clientConnected_t; typedef enum { SPECTATOR_NOT, SPECTATOR_FREE, SPECTATOR_FOLLOW, SPECTATOR_SCOREBOARD } spectatorState_t; typedef enum { TEAM_BEGIN, // Beginning a team game, spawn at base TEAM_ACTIVE // Now actively playing } playerTeamStateState_t; typedef struct { playerTeamStateState_t state; int location; int captures; int basedefense; int carrierdefense; int flagrecovery; int fragcarrier; int assists; float lasthurtcarrier; float lastreturnedflag; float flagsince; float lastfraggedcarrier; } playerTeamState_t; // the auto following clients don't follow a specific client // number, but instead follow the first two active players #define FOLLOW_ACTIVE1 -1 #define FOLLOW_ACTIVE2 -2 // client data that stays across multiple levels or tournament restarts // this is achieved by writing all the data to cvar strings at game shutdown // time and reading them back at connection time. Anything added here // MUST be dealt with in G_InitSessionData() / G_ReadSessionData() / G_WriteSessionData() typedef struct { team_t sessionTeam; int spectatorNum; // for determining next-in-line to play spectatorState_t spectatorState; int spectatorClient; // for chasecam and follow mode int wins, losses; // tournament stats qboolean teamLeader; // true when this client is a team leader } clientSession_t; // #define MAX_NETNAME 36 #define MAX_VOTE_COUNT "3" //unlagged - true ping #define NUM_PING_SAMPLES 64 //unlagged - true ping // client data that stays across multiple respawns, but is cleared // on each level change or team change at ClientBegin() typedef struct { clientConnected_t connected; usercmd_t cmd; // we would lose angles if not persistant qboolean localClient; // true if "ip" info key is "localhost" qboolean initialSpawn; // the first spawn should be at a cool location qboolean predictItemPickup; // based on cg_predictItems userinfo qboolean pmoveFixed; // char netname[MAX_NETNAME]; int maxHealth; // for handicapping int enterTime; // level.time the client entered the game playerTeamState_t teamState; // status in teamplay games int voteCount; // to prevent people from constantly calling votes int teamVoteCount; // to prevent people from constantly calling votes qboolean teamInfo; // send team overlay updates? //elimination: int roundReached; //Only spawn if we are new to this round int livesLeft; //lives in LMS //unlagged - client options // these correspond with variables in the userinfo string int delag; // int debugDelag; int cmdTimeNudge; //unlagged - client options //unlagged - lag simulation #2 /* int latentSnaps; int latentCmds; int plOut; usercmd_t cmdqueue[MAX_LATENT_CMDS]; int cmdhead;*/ //unlagged - lag simulation #2 //unlagged - true ping int realPing; int pingsamples[NUM_PING_SAMPLES]; int samplehead; //unlagged - true ping //KK-OAX Killing Sprees/Multikills int killstreak; int deathstreak; qboolean onSpree; int multiKillCount; //KK-OAX Admin Stuff char guid[ 33 ]; char ip[ 40 ]; qboolean muted; qboolean disoriented; qboolean wasdisoriented; int adminLevel; // flood protection int floodDemerits; int floodTime; //Used To Track Name Changes int nameChangeTime; int nameChanges; } clientPersistant_t; //unlagged - backward reconciliation #1 // the size of history we'll keep #define NUM_CLIENT_HISTORY 17 // everything we need to know to backward reconcile typedef struct { vec3_t mins, maxs; vec3_t currentOrigin; int leveltime; } clientHistory_t; //unlagged - backward reconciliation #1 // this structure is cleared on each ClientSpawn(), // except for 'client->pers' and 'client->sess' struct gclient_s { // ps MUST be the first element, because the server expects it playerState_t ps; // communicated by server to clients // the rest of the structure is private to game clientPersistant_t pers; clientSession_t sess; qboolean readyToExit; // wishes to leave the intermission qboolean noclip; //Unlagged - commented out - handled differently //int lastCmdTime; // level.time of last usercmd_t, for EF_CONNECTION // we can't just use pers.lastCommand.time, because // of the g_sycronousclients case int buttons; int oldbuttons; int latched_buttons; vec3_t oldOrigin; // sum up damage over an entire frame, so // shotgun blasts give a single big kick int damage_armor; // damage absorbed by armor int damage_blood; // damage taken out of health int damage_knockback; // impact damage vec3_t damage_from; // origin for vector calculation qboolean damage_fromWorld; // if true, don't use the damage_from vector int accurateCount; // for "impressive" reward sound int accuracy_shots; // total number of shots int accuracy_hits; // total number of hits // int lastkilled_client; // last client that this client killed int lasthurt_client; // last client that damaged this client int lasthurt_mod; // type of damage the client did // timers int respawnTime; // can respawn when time > this, force after g_forcerespwan int inactivityTime; // kick players when time > this qboolean inactivityWarning; // qtrue if the five seoond warning has been given int rewardTime; // clear the EF_AWARD_IMPRESSIVE, etc when time > this int airOutTime; int lastKillTime; // for multiple kill rewards qboolean fireHeld; // used for hook gentity_t *hook; // grapple hook if out int switchTeamTime; // time the player switched teams // timeResidual is used to handle events that happen every second // like health / armor countdowns and regeneration int timeResidual; gentity_t *persistantPowerup; int portalID; int ammoTimes[WP_NUM_WEAPONS]; int invulnerabilityTime; char *areabits; qboolean isEliminated; //Has been killed in this round //New vote system. The votes are saved in the client info, so we know who voted on what and can cancel votes on leave. //0=not voted, 1=voted yes, -1=voted no int vote; int lastSentFlying; //The last client that sent the player flying int lastSentFlyingTime; //So we can time out //unlagged - backward reconciliation #1 // the serverTime the button was pressed // (stored before pmove_fixed changes serverTime) int attackTime; // the head of the history queue int historyHead; // the history queue clientHistory_t history[NUM_CLIENT_HISTORY]; // the client's saved position clientHistory_t saved; // used to restore after time shift // an approximation of the actual server time we received this // command (not in 50ms increments) int frameOffset; //unlagged - backward reconciliation #1 //unlagged - smooth clients #1 // the last frame number we got an update from this client int lastUpdateFrame; //unlagged - smooth clients #1 qboolean spawnprotected; int accuracy[WP_NUM_WEAPONS][2]; }; // // this structure is cleared as each map is entered // #define MAX_SPAWN_VARS 64 #define MAX_SPAWN_VARS_CHARS 4096 typedef struct { struct gclient_s *clients; // [maxclients] struct gentity_s *gentities; int gentitySize; int num_entities; // current number, <= MAX_GENTITIES int warmupTime; // restart match at this time fileHandle_t logFile; // store latched cvars here that we want to get at often int maxclients; int framenum; int time; // in msec int previousTime; // so movers can back up when blocked int startTime; // level.time the map was started int teamScores[TEAM_NUM_TEAMS]; int lastTeamLocationTime; // last time of client team location update qboolean newSession; // don't use any old session data, because // we changed gametype qboolean restarted; // waiting for a map_restart to fire int numConnectedClients; int numNonSpectatorClients; // includes connecting clients int numPlayingClients; // connected, non-spectators int sortedClients[MAX_CLIENTS]; // sorted by score int follow1, follow2; // clientNums for auto-follow spectators int snd_fry; // sound index for standing in lava int warmupModificationCount; // for detecting if g_warmup is changed // voting state char voteString[MAX_STRING_CHARS]; char voteDisplayString[MAX_STRING_CHARS]; int voteTime; // level.time vote was called int voteExecuteTime; // time the vote is executed int voteYes; int voteNo; int numVotingClients; // set by CountVotes int voteKickClient; // if non-negative the current vote is about this client. int voteKickType; // if 1 = ban (execute ban) // team voting state char teamVoteString[2][MAX_STRING_CHARS]; int teamVoteTime[2]; // level.time vote was called int teamVoteYes[2]; int teamVoteNo[2]; int numteamVotingClients[TEAM_NUM_TEAMS];// set by CalculateRanks // spawn variables qboolean spawning; // the G_Spawn*() functions are valid int numSpawnVars; char *spawnVars[MAX_SPAWN_VARS][2]; // key / value pairs int numSpawnVarChars; char spawnVarChars[MAX_SPAWN_VARS_CHARS]; // intermission state int intermissionQueued; // intermission was qualified, but // wait INTERMISSION_DELAY_TIME before // actually going there so the last // frag can be watched. Disable future // kills during this delay int intermissiontime; // time the intermission was started char *changemap; qboolean readyToExit; // at least one client wants to exit int exitTime; vec3_t intermission_origin; // also used for spectator spawns vec3_t intermission_angle; qboolean locationLinked; // target_locations get linked gentity_t *locationHead; // head of the location list int bodyQueIndex; // dead bodies gentity_t *bodyQue[BODY_QUEUE_SIZE]; int portalSequence; //Added for elimination: int roundStartTime; //time the current round was started int roundNumber; //The round number we have reached int roundNumberStarted; //1 less than roundNumber if we are allowed to spawn int roundRedPlayers; //How many players was there at start of round int roundBluePlayers; //used to find winners in a draw. qboolean roundRespawned; //We have respawned for this round! int eliminationSides; //Random, change red/blue bases //Added for Double Domination //Points get status: TEAM_FREE for not taking, TEAM_RED/TEAM_BLUE for taken and TEAM_NONE for not spawned yet int pointStatusA; //Status of the RED (A) domination point int pointStatusB; //Status of the BLUE (B) doimination point int timeTaken; //Time team started having both points //use roundStartTime for telling, then the points respawn //Added for standard domination int pointStatusDom[MAX_DOMINATION_POINTS]; //Holds the owner of all the points int dom_scoreGiven; //Number of times we have provided scores int domination_points_count; char domination_points_names[MAX_DOMINATION_POINTS][MAX_DOMINATION_POINTS_NAMES]; //Added to keep track of challenges (can only be completed against humanplayers) qboolean hadBots; //There was bots in the level int teamSize; //The highest number of players on the least populated team when there was most players //unlagged - backward reconciliation #4 // actual time this server frame started int frameStartTime; //unlagged - backward reconciliation #4 //KK-OAX Storing upper bounds of spree/multikill arrays int kSpreeUBound; int dSpreeUBound; int mKillUBound; //KK-OAX Storing g_spreeDiv to avoid dividing by 0. int spreeDivisor; qboolean RedTeamLocked; qboolean BlueTeamLocked; qboolean FFALocked; //Obelisk tell int healthRedObelisk; //health in percent int healthBlueObelisk; //helth in percent qboolean MustSendObeliskHealth; //Health has changed } level_locals_t; //KK-OAX These are some Print Shortcuts for KillingSprees and Admin //KK-Moved to g_admin.h //Prints to All when using "va()" in conjunction. //#define AP(x) trap_SendServerCommand(-1, x) // // g_spawn.c // qboolean G_SpawnString( const char *key, const char *defaultString, char **out ); // spawn string returns a temporary reference, you must CopyString() if you want to keep it qboolean G_SpawnFloat( const char *key, const char *defaultString, float *out ); qboolean G_SpawnInt( const char *key, const char *defaultString, int *out ); qboolean G_SpawnVector( const char *key, const char *defaultString, float *out ); void G_SpawnEntitiesFromString( void ); char *G_NewString( const char *string ); // // g_cmds.c // void Cmd_Score_f (gentity_t *ent); void StopFollowing( gentity_t *ent ); void BroadcastTeamChange( gclient_t *client, int oldTeam ); void SetTeam( gentity_t *ent, char *s ); void Cmd_FollowCycle_f( gentity_t *ent ); //KK-OAX Changed to match definition char *ConcatArgs( int start ); //KK-OAX This declaration moved from g_svccmds.c //KK-OAX Added this to make accessible from g_svcmds_ext.c void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ); // KK-OAX Added these in a seperate file to keep g_cmds.c familiar. // g_cmds_ext.c // int G_SayArgc( void ); qboolean G_SayArgv( int n, char *buffer, int bufferLength ); char *G_SayConcatArgs( int start ); void G_DecolorString( char *in, char *out, int len ); void G_MatchOnePlayer( int *plist, int num, char *err, int len ); void G_SanitiseString( char *in, char *out, int len ); int G_ClientNumbersFromString( char *s, int *plist, int max ); int G_FloodLimited( gentity_t *ent ); //void QDECL G_AdminMessage( const char *prefix, const char *fmt, ... ) // ^^ Do Not Need to Declare--Just for Documentation of where it is. void Cmd_AdminMessage_f( gentity_t *ent ); int G_ClientNumberFromString( char *s ); qboolean G_ClientIsLagging( gclient_t *client ); void SanitizeString( char *in, char *out ); // KK-OAX Added this for common file stuff between Admin and Sprees. // g_fileops.c // void readFile_int( char **cnf, int *v ); void readFile_string( char **cnf, char *s, int size ); void writeFile_int( int v, fileHandle_t f ); void writeFile_string( char *s, fileHandle_t f ); // // g_items.c // void G_CheckTeamItems( void ); void G_RunItem( gentity_t *ent ); void RespawnItem( gentity_t *ent ); void UseHoldableItem( gentity_t *ent ); void PrecacheItem (gitem_t *it); gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ); gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ); void SetRespawn (gentity_t *ent, float delay); void G_SpawnItem (gentity_t *ent, gitem_t *item); void FinishSpawningItem( gentity_t *ent ); void Think_Weapon (gentity_t *ent); int ArmorIndex (gentity_t *ent); void Add_Ammo (gentity_t *ent, int weapon, int count); void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace); void ClearRegisteredItems( void ); void RegisterItem( gitem_t *item ); void SaveRegisteredItems( void ); // // g_utils.c // int G_ModelIndex( char *name ); int G_SoundIndex( char *name ); void G_TeamCommand( team_t team, char *cmd ); void G_KillBox (gentity_t *ent); gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match); gentity_t *G_PickTarget (char *targetname); void G_UseTargets (gentity_t *ent, gentity_t *activator); void G_SetMovedir ( vec3_t angles, vec3_t movedir); void G_InitGentity( gentity_t *e ); gentity_t *G_Spawn (void); gentity_t *G_TempEntity( vec3_t origin, int event ); void G_Sound( gentity_t *ent, int channel, int soundIndex ); //KK-OAX For Playing Sounds Globally void G_GlobalSound( int soundIndex ); void G_FreeEntity( gentity_t *e ); qboolean G_EntitiesFree( void ); void G_TouchTriggers (gentity_t *ent); void G_TouchSolids (gentity_t *ent); float *tv (float x, float y, float z); char *vtos( const vec3_t v ); float vectoyaw( const vec3_t vec ); void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ); void G_AddEvent( gentity_t *ent, int event, int eventParm ); void G_SetOrigin( gentity_t *ent, vec3_t origin ); void AddRemap(const char *oldShader, const char *newShader, float timeOffset); const char *BuildShaderStateConfig( void ); // // g_combat.c // qboolean CanDamage (gentity_t *targ, vec3_t origin); void G_Damage (gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod); qboolean G_RadiusDamage (vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod); int G_InvulnerabilityEffect( gentity_t *targ, vec3_t dir, vec3_t point, vec3_t impactpoint, vec3_t bouncedir ); void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ); void TossClientItems( gentity_t *self ); void TossClientPersistantPowerups( gentity_t *self ); void TossClientCubes( gentity_t *self ); // damage flags #define DAMAGE_RADIUS 0x00000001 // damage was indirect #define DAMAGE_NO_ARMOR 0x00000002 // armour does not protect from this damage #define DAMAGE_NO_KNOCKBACK 0x00000004 // do not affect velocity, just view angles #define DAMAGE_NO_PROTECTION 0x00000008 // armor, shields, invulnerability, and godmode have no effect #define DAMAGE_NO_TEAM_PROTECTION 0x00000010 // armor, shields, invulnerability, and godmode have no effect // // g_missile.c // void G_RunMissile( gentity_t *ent ); void ProximityMine_RemoveAll( void ); gentity_t *fire_blaster (gentity_t *self, vec3_t start, vec3_t aimdir); gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t aimdir); gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t aimdir); gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir); gentity_t *fire_bfg (gentity_t *self, vec3_t start, vec3_t dir); gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir); gentity_t *fire_nail( gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up ); gentity_t *fire_prox( gentity_t *self, vec3_t start, vec3_t aimdir ); // // g_mover.c // void G_RunMover( gentity_t *ent ); void Touch_DoorTrigger( gentity_t *ent, gentity_t *other, trace_t *trace ); // // g_trigger.c // void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ); // // g_misc.c // void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ); void DropPortalSource( gentity_t *ent ); void DropPortalDestination( gentity_t *ent ); // // g_weapon.c // qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ); void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ); //unlagged - attack prediction #3 // we're making this available to both games void SnapVectorTowards( vec3_t v, vec3_t to ); //unlagged - attack prediction #3 qboolean CheckGauntletAttack( gentity_t *ent ); void Weapon_HookFree (gentity_t *ent); void Weapon_HookThink (gentity_t *ent); //unlagged - g_unlagged.c void G_ResetHistory( gentity_t *ent ); void G_StoreHistory( gentity_t *ent ); void G_TimeShiftAllClients( int time, gentity_t *skip ); void G_UnTimeShiftAllClients( gentity_t *skip ); void G_DoTimeShiftFor( gentity_t *ent ); void G_UndoTimeShiftFor( gentity_t *ent ); void G_UnTimeShiftClient( gentity_t *client ); void G_PredictPlayerMove( gentity_t *ent, float frametime ); //unlagged - g_unlagged.c // // g_client.c // team_t TeamCount( int ignoreClientNum, int team ); team_t TeamLivingCount( int ignoreClientNum, int team ); //Elimination team_t TeamHealthCount( int ignoreClientNum, int team ); //Elimination void RespawnAll(void); //For round elimination void RespawnDead(void); void EnableWeapons(void); void DisableWeapons(void); void EndEliminationRound(void); void LMSpoint(void); //void wins2score(void); int TeamLeader( int team ); team_t PickTeam( int ignoreClientNum ); void SetClientViewAngle( gentity_t *ent, vec3_t angle ); gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ); void CopyToBodyQue( gentity_t *ent ); void ClientRespawn(gentity_t *ent); void BeginIntermission (void); void InitClientPersistant (gclient_t *client); void InitClientResp (gclient_t *client); void InitBodyQue (void); void ClientSpawn( gentity_t *ent ); void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod); void AddScore( gentity_t *ent, vec3_t origin, int score ); void CalculateRanks( void ); qboolean SpotWouldTelefrag( gentity_t *spot ); // // g_svcmds.c // qboolean ConsoleCommand( void ); void G_ProcessIPBans(void); qboolean G_FilterPacket (char *from); //KK-OAX Added this to make accessible from g_svcmds_ext.c gclient_t *ClientForString( const char *s ); // // g_weapon.c // void FireWeapon( gentity_t *ent ); void G_StartKamikaze( gentity_t *ent ); // // p_hud.c // void MoveClientToIntermission (gentity_t *client); void G_SetStats (gentity_t *ent); // // g_cmds.c // Also another place /Sago void DoubleDominationScoreTimeMessage( gentity_t *ent ); void YourTeamMessage( gentity_t *ent); void AttackingTeamMessage( gentity_t *ent ); void ObeliskHealthMessage( void ); void DeathmatchScoreboardMessage (gentity_t *client); void EliminationMessage (gentity_t *client); void RespawnTimeMessage(gentity_t *ent, int time); void DominationPointNamesMessage (gentity_t *client); void DominationPointStatusMessage( gentity_t *ent ); void ChallengeMessage( gentity_t *ent, int challengeNumber ); void SendCustomVoteCommands(int clientNum); // // g_pweapon.c // // // g_main.c // void FindIntermissionPoint( void ); void SetLeader(int team, int client); void CheckTeamLeader( int team ); void G_RunThink (gentity_t *ent); void AddTournamentQueue(gclient_t *client); void ExitLevel( void ); void QDECL G_LogPrintf( const char *fmt, ... ); void SendScoreboardMessageToAllClients( void ); void SendEliminationMessageToAllClients( void ); void SendDDtimetakenMessageToAllClients( void ); void SendDominationPointsStatusMessageToAllClients( void ); void SendYourTeamMessageToTeam( team_t team ); void QDECL G_Printf( const char *fmt, ... ); void QDECL G_Error( const char *fmt, ... ) __attribute__((noreturn)); //KK-OAX Made Accessible for g_admin.c void LogExit( const char *string ); void CheckTeamVote( int team ); // // g_client.c // char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ); void ClientUserinfoChanged( int clientNum ); void ClientDisconnect( int clientNum ); void ClientBegin( int clientNum ); void ClientCommand( int clientNum ); // // g_active.c // void ClientThink( int clientNum ); void ClientEndFrame( gentity_t *ent ); void G_RunClient( gentity_t *ent ); // // g_team.c // qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ); void Team_CheckDroppedItem( gentity_t *dropped ); qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker ); void ShuffleTeams(void); //KK-OAX Added for Command Handling Changes (r24) team_t G_TeamFromString( char *str ); //KK-OAX Removed these in Code in favor of bg_alloc.c from Tremulous // g_mem.c // //void *G_Alloc( int size ); //void G_InitMemory( void ); //KK-OAX This was moved // bg_alloc.c // void Svcmd_GameMem_f( void ); // // g_session.c // void G_ReadSessionData( gclient_t *client ); void G_InitSessionData( gclient_t *client, char *userinfo ); void G_InitWorldSession( void ); void G_WriteSessionData( void ); // // g_arenas.c // void UpdateTournamentInfo( void ); void SpawnModelsOnVictoryPads( void ); void Svcmd_AbortPodium_f( void ); // // g_bot.c // void G_InitBots( qboolean restart ); char *G_GetBotInfoByNumber( int num ); char *G_GetBotInfoByName( const char *name ); void G_CheckBotSpawn( void ); void G_RemoveQueuedBotBegin( int clientNum ); qboolean G_BotConnect( int clientNum, qboolean restart ); void Svcmd_AddBot_f( void ); void Svcmd_BotList_f( void ); void BotInterbreedEndMatch( void ); // // g_playerstore.c // void PlayerStoreInit( void ); void PlayerStore_store(char* guid, playerState_t ps); void PlayerStore_restore(char* guid, playerState_t *ps); // // g_vote.c // int allowedVote(char *commandStr); void CheckVote( void ); void CountVotes( void ); void ClientLeaving(int clientNumber); #define MAX_MAPNAME 32 #define MAPS_PER_PAGE 10 #define MAX_MAPNAME_BUFFER MAX_MAPNAME*600 #define MAX_MAPNAME_LENGTH 34 #define MAX_CUSTOMNAME MAX_MAPNAME #define MAX_CUSTOMCOMMAND 100 #define MAX_CUSTOMDISPLAYNAME 50 typedef struct { int pagenumber; char mapname[MAPS_PER_PAGE][MAX_MAPNAME]; } t_mappage; typedef struct { char votename[MAX_CUSTOMNAME]; //Used like "/callvote custom VOTENAME" char displayname[MAX_CUSTOMDISPLAYNAME]; //Displayed during voting char command[MAX_CUSTOMCOMMAND]; //The command executed } t_customvote; extern char custom_vote_info[1024]; extern t_mappage getMappage(int page); extern int allowedMap(char *mapname); extern int allowedGametype(char *gametypeStr); extern int allowedTimelimit(int limit); extern int allowedFraglimit(int limit); extern int VoteParseCustomVotes( void ); extern t_customvote getCustomVote(char* votecommand); // ai_main.c #define MAX_FILEPATH 144 //bot settings typedef struct bot_settings_s { char characterfile[MAX_FILEPATH]; float skill; char team[MAX_FILEPATH]; } bot_settings_t; int BotAISetup( int restart ); int BotAIShutdown( int restart ); int BotAILoadMap( int restart ); int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart); int BotAIShutdownClient( int client, qboolean restart ); int BotAIStartFrame( int time ); void BotTestAAS(vec3_t origin); #include "g_team.h" // teamplay specific stuff extern level_locals_t level; extern gentity_t g_entities[MAX_GENTITIES]; #define FOFS(x) ((size_t)&(((gentity_t *)0)->x)) //CVARS extern vmCvar_t g_gametype; extern vmCvar_t g_dedicated; extern vmCvar_t g_cheats; extern vmCvar_t g_maxclients; // allow this many total, including spectators extern vmCvar_t g_maxGameClients; // allow this many active extern vmCvar_t g_restarted; extern vmCvar_t g_dmflags; extern vmCvar_t g_videoflags; extern vmCvar_t g_elimflags; extern vmCvar_t g_voteflags; extern vmCvar_t g_fraglimit; extern vmCvar_t g_timelimit; extern vmCvar_t g_capturelimit; extern vmCvar_t g_friendlyFire; extern vmCvar_t g_password; extern vmCvar_t g_needpass; extern vmCvar_t g_gravity; extern vmCvar_t g_gravityModifier; extern vmCvar_t g_damageModifier; extern vmCvar_t g_speed; extern vmCvar_t g_knockback; extern vmCvar_t g_quadfactor; extern vmCvar_t g_forcerespawn; extern vmCvar_t g_respawntime; extern vmCvar_t g_inactivity; extern vmCvar_t g_debugMove; extern vmCvar_t g_debugAlloc; extern vmCvar_t g_debugDamage; extern vmCvar_t g_weaponRespawn; extern vmCvar_t g_weaponTeamRespawn; extern vmCvar_t g_synchronousClients; extern vmCvar_t g_motd; extern vmCvar_t g_motdfile; extern vmCvar_t g_votemaps; extern vmCvar_t g_votecustom; extern vmCvar_t g_warmup; extern vmCvar_t g_doWarmup; extern vmCvar_t g_blood; extern vmCvar_t g_allowVote; extern vmCvar_t g_teamAutoJoin; extern vmCvar_t g_teamForceBalance; extern vmCvar_t g_banIPs; extern vmCvar_t g_filterBan; extern vmCvar_t g_obeliskHealth; extern vmCvar_t g_obeliskRegenPeriod; extern vmCvar_t g_obeliskRegenAmount; extern vmCvar_t g_obeliskRespawnDelay; extern vmCvar_t g_cubeTimeout; extern vmCvar_t g_smoothClients; extern vmCvar_t pmove_fixed; extern vmCvar_t pmove_msec; extern vmCvar_t pmove_float; extern vmCvar_t g_rankings; #ifdef MISSIONPACK extern vmCvar_t g_singlePlayer; extern vmCvar_t g_redteam; extern vmCvar_t g_blueteam; #endif extern vmCvar_t g_enableDust; extern vmCvar_t g_enableBreath; extern vmCvar_t g_proxMineTimeout; extern vmCvar_t g_music; extern vmCvar_t g_spawnprotect; //elimination: extern vmCvar_t g_elimination_selfdamage; extern vmCvar_t g_elimination_startHealth; extern vmCvar_t g_elimination_startArmor; extern vmCvar_t g_elimination_bfg; extern vmCvar_t g_elimination_grapple; extern vmCvar_t g_elimination_roundtime; extern vmCvar_t g_elimination_warmup; extern vmCvar_t g_elimination_activewarmup; extern vmCvar_t g_elimination_allgametypes; extern vmCvar_t g_elimination_machinegun; extern vmCvar_t g_elimination_shotgun; extern vmCvar_t g_elimination_grenade; extern vmCvar_t g_elimination_rocket; extern vmCvar_t g_elimination_railgun; extern vmCvar_t g_elimination_lightning; extern vmCvar_t g_elimination_plasmagun; extern vmCvar_t g_elimination_chain; extern vmCvar_t g_elimination_mine; extern vmCvar_t g_elimination_nail; //If lockspectator: 0=no limit, 1 = cannot follow enemy, 2 = must follow friend extern vmCvar_t g_elimination_lockspectator; extern vmCvar_t g_rockets; //new in elimination Beta2 extern vmCvar_t g_instantgib; extern vmCvar_t g_vampire; extern vmCvar_t g_vampireMaxHealth; //new in elimination Beta3 extern vmCvar_t g_regen; //Free for all gametype extern int g_ffa_gt; //0 = TEAM GAME, 1 = FFA, 2 = TEAM GAME without bases extern vmCvar_t g_lms_lives; extern vmCvar_t g_lms_mode; //How do we score: 0 = One Survivor get a point, 1 = same but without overtime, 2 = one point for each player killed (+overtime), 3 = same without overtime extern vmCvar_t g_elimination_ctf_oneway; //Only attack in one direction (level.eliminationSides+level.roundNumber)%2 == 0 red attacks extern vmCvar_t g_awardpushing; //The server can decide if players are awarded for pushing people in lave etc. extern vmCvar_t g_persistantpowerups; extern vmCvar_t g_catchup; //Favors the week players extern vmCvar_t g_autonextmap; //Autochange map extern vmCvar_t g_mappools; //mappools to be used for autochange extern vmCvar_t g_voteNames; extern vmCvar_t g_voteBan; extern vmCvar_t g_voteGametypes; extern vmCvar_t g_voteMinTimelimit; extern vmCvar_t g_voteMaxTimelimit; extern vmCvar_t g_voteMinFraglimit; extern vmCvar_t g_voteMaxFraglimit; extern vmCvar_t g_maxvotes; extern vmCvar_t g_humanplayers; //used for voIP extern vmCvar_t g_redTeamClientNumbers; extern vmCvar_t g_blueTeamClientNumbers; //unlagged - server options // some new server-side variables extern vmCvar_t g_delagHitscan; extern vmCvar_t g_truePing; // this is for convenience - using "sv_fps.integer" is nice :) extern vmCvar_t sv_fps; extern vmCvar_t g_lagLightning; //unlagged - server options //KK-OAX Killing Sprees extern vmCvar_t g_sprees; //Used for specifiying the config file extern vmCvar_t g_altExcellent; //Turns on Multikills instead of Excellent extern vmCvar_t g_spreeDiv; // Interval of a "streak" that form the spree triggers //KK-OAX Command/Chat Flooding/Spamming extern vmCvar_t g_floodMaxDemerits; extern vmCvar_t g_floodMinTime; //KK-OAX Admin extern vmCvar_t g_admin; extern vmCvar_t g_adminLog; extern vmCvar_t g_adminParseSay; extern vmCvar_t g_adminNameProtect; extern vmCvar_t g_adminTempBan; extern vmCvar_t g_adminMaxBan; //KK-OAX Admin-Like extern vmCvar_t g_specChat; extern vmCvar_t g_publicAdminMessages; extern vmCvar_t g_maxWarnings; extern vmCvar_t g_warningExpire; extern vmCvar_t g_minNameChangePeriod; extern vmCvar_t g_maxNameChanges; void trap_Printf( const char *fmt ); void trap_Error( const char *fmt ) __attribute__((noreturn)); int trap_Milliseconds( void ); int trap_RealTime( qtime_t *qtime ); int trap_Argc( void ); void trap_Argv( int n, char *buffer, int bufferLength ); void trap_Args( char *buffer, int bufferLength ); int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); void trap_FS_Read( void *buffer, int len, fileHandle_t f ); void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); void trap_FS_FCloseFile( fileHandle_t f ); int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t void trap_SendConsoleCommand( int exec_when, const char *text ); void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags ); void trap_Cvar_Update( vmCvar_t *cvar ); void trap_Cvar_Set( const char *var_name, const char *value ); int trap_Cvar_VariableIntegerValue( const char *var_name ); float trap_Cvar_VariableValue( const char *var_name ); void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void trap_LocateGameData( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t, playerState_t *gameClients, int sizeofGameClient ); void trap_DropClient( int clientNum, const char *reason ); void trap_SendServerCommand( int clientNum, const char *text ); void trap_SetConfigstring( int num, const char *string ); void trap_GetConfigstring( int num, char *buffer, int bufferSize ); void trap_GetUserinfo( int num, char *buffer, int bufferSize ); void trap_SetUserinfo( int num, const char *buffer ); void trap_GetServerinfo( char *buffer, int bufferSize ); void trap_SetBrushModel( gentity_t *ent, const char *name ); void trap_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask ); int trap_PointContents( const vec3_t point, int passEntityNum ); qboolean trap_InPVS( const vec3_t p1, const vec3_t p2 ); qboolean trap_InPVSIgnorePortals( const vec3_t p1, const vec3_t p2 ); void trap_AdjustAreaPortalState( gentity_t *ent, qboolean open ); qboolean trap_AreasConnected( int area1, int area2 ); void trap_LinkEntity( gentity_t *ent ); void trap_UnlinkEntity( gentity_t *ent ); int trap_EntitiesInBox( const vec3_t mins, const vec3_t maxs, int *entityList, int maxcount ); qboolean trap_EntityContact( const vec3_t mins, const vec3_t maxs, const gentity_t *ent ); int trap_BotAllocateClient( void ); void trap_BotFreeClient( int clientNum ); void trap_GetUsercmd( int clientNum, usercmd_t *cmd ); qboolean trap_GetEntityToken( char *buffer, int bufferSize ); int trap_DebugPolygonCreate(int color, int numPoints, vec3_t *points); void trap_DebugPolygonDelete(int id); int trap_BotLibSetup( void ); int trap_BotLibShutdown( void ); int trap_BotLibVarSet(char *var_name, char *value); int trap_BotLibVarGet(char *var_name, char *value, int size); int trap_BotLibDefine(char *string); int trap_BotLibStartFrame(float time); int trap_BotLibLoadMap(const char *mapname); int trap_BotLibUpdateEntity(int ent, void /* struct bot_updateentity_s */ *bue); int trap_BotLibTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3); int trap_BotGetSnapshotEntity( int clientNum, int sequence ); int trap_BotGetServerCommand(int clientNum, char *message, int size); void trap_BotUserCommand(int client, usercmd_t *ucmd); int trap_AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); int trap_AAS_AreaInfo( int areanum, void /* struct aas_areainfo_s */ *info ); void trap_AAS_EntityInfo(int entnum, void /* struct aas_entityinfo_s */ *info); int trap_AAS_Initialized(void); void trap_AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); float trap_AAS_Time(void); int trap_AAS_PointAreaNum(vec3_t point); int trap_AAS_PointReachabilityAreaIndex(vec3_t point); int trap_AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); int trap_AAS_PointContents(vec3_t point); int trap_AAS_NextBSPEntity(int ent); int trap_AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); int trap_AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); int trap_AAS_FloatForBSPEpairKey(int ent, char *key, float *value); int trap_AAS_IntForBSPEpairKey(int ent, char *key, int *value); int trap_AAS_AreaReachability(int areanum); int trap_AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); int trap_AAS_EnableRoutingArea( int areanum, int enable ); int trap_AAS_PredictRoute(void /*struct aas_predictroute_s*/ *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum); int trap_AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, void /*struct aas_altroutegoal_s*/ *altroutegoals, int maxaltroutegoals, int type); int trap_AAS_Swimming(vec3_t origin); int trap_AAS_PredictClientMovement(void /* aas_clientmove_s */ *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize); void trap_EA_Say(int client, char *str); void trap_EA_SayTeam(int client, char *str); void trap_EA_Command(int client, char *command); void trap_EA_Action(int client, int action); void trap_EA_Gesture(int client); void trap_EA_Talk(int client); void trap_EA_Attack(int client); void trap_EA_Use(int client); void trap_EA_Respawn(int client); void trap_EA_Crouch(int client); void trap_EA_MoveUp(int client); void trap_EA_MoveDown(int client); void trap_EA_MoveForward(int client); void trap_EA_MoveBack(int client); void trap_EA_MoveLeft(int client); void trap_EA_MoveRight(int client); void trap_EA_SelectWeapon(int client, int weapon); void trap_EA_Jump(int client); void trap_EA_DelayedJump(int client); void trap_EA_Move(int client, vec3_t dir, float speed); void trap_EA_View(int client, vec3_t viewangles); void trap_EA_EndRegular(int client, float thinktime); void trap_EA_GetInput(int client, float thinktime, void /* struct bot_input_s */ *input); void trap_EA_ResetInput(int client); int trap_BotLoadCharacter(char *charfile, float skill); void trap_BotFreeCharacter(int character); float trap_Characteristic_Float(int character, int index); float trap_Characteristic_BFloat(int character, int index, float min, float max); int trap_Characteristic_Integer(int character, int index); int trap_Characteristic_BInteger(int character, int index, int min, int max); void trap_Characteristic_String(int character, int index, char *buf, int size); int trap_BotAllocChatState(void); void trap_BotFreeChatState(int handle); void trap_BotQueueConsoleMessage(int chatstate, int type, char *message); void trap_BotRemoveConsoleMessage(int chatstate, int handle); int trap_BotNextConsoleMessage(int chatstate, void /* struct bot_consolemessage_s */ *cm); int trap_BotNumConsoleMessages(int chatstate); void trap_BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7 ); int trap_BotNumInitialChats(int chatstate, char *type); int trap_BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7 ); int trap_BotChatLength(int chatstate); void trap_BotEnterChat(int chatstate, int client, int sendto); void trap_BotGetChatMessage(int chatstate, char *buf, int size); int trap_StringContains(char *str1, char *str2, int casesensitive); int trap_BotFindMatch(char *str, void /* struct bot_match_s */ *match, unsigned long int context); void trap_BotMatchVariable(void /* struct bot_match_s */ *match, int variable, char *buf, int size); void trap_UnifyWhiteSpaces(char *string); void trap_BotReplaceSynonyms(char *string, unsigned long int context); int trap_BotLoadChatFile(int chatstate, char *chatfile, char *chatname); void trap_BotSetChatGender(int chatstate, int gender); void trap_BotSetChatName(int chatstate, char *name, int client); void trap_BotResetGoalState(int goalstate); void trap_BotRemoveFromAvoidGoals(int goalstate, int number); void trap_BotResetAvoidGoals(int goalstate); void trap_BotPushGoal(int goalstate, void /* struct bot_goal_s */ *goal); void trap_BotPopGoal(int goalstate); void trap_BotEmptyGoalStack(int goalstate); void trap_BotDumpAvoidGoals(int goalstate); void trap_BotDumpGoalStack(int goalstate); void trap_BotGoalName(int number, char *name, int size); int trap_BotGetTopGoal(int goalstate, void /* struct bot_goal_s */ *goal); int trap_BotGetSecondGoal(int goalstate, void /* struct bot_goal_s */ *goal); int trap_BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags); int trap_BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, void /* struct bot_goal_s */ *ltg, float maxtime); int trap_BotTouchingGoal(vec3_t origin, void /* struct bot_goal_s */ *goal); int trap_BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, void /* struct bot_goal_s */ *goal); int trap_BotGetNextCampSpotGoal(int num, void /* struct bot_goal_s */ *goal); int trap_BotGetMapLocationGoal(char *name, void /* struct bot_goal_s */ *goal); int trap_BotGetLevelItemGoal(int index, char *classname, void /* struct bot_goal_s */ *goal); float trap_BotAvoidGoalTime(int goalstate, int number); void trap_BotSetAvoidGoalTime(int goalstate, int number, float avoidtime); void trap_BotInitLevelItems(void); void trap_BotUpdateEntityItems(void); int trap_BotLoadItemWeights(int goalstate, char *filename); void trap_BotFreeItemWeights(int goalstate); void trap_BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child); void trap_BotSaveGoalFuzzyLogic(int goalstate, char *filename); void trap_BotMutateGoalFuzzyLogic(int goalstate, float range); int trap_BotAllocGoalState(int state); void trap_BotFreeGoalState(int handle); void trap_BotResetMoveState(int movestate); void trap_BotMoveToGoal(void /* struct bot_moveresult_s */ *result, int movestate, void /* struct bot_goal_s */ *goal, int travelflags); int trap_BotMoveInDirection(int movestate, vec3_t dir, float speed, int type); void trap_BotResetAvoidReach(int movestate); void trap_BotResetLastAvoidReach(int movestate); int trap_BotReachabilityArea(vec3_t origin, int testground); int trap_BotMovementViewTarget(int movestate, void /* struct bot_goal_s */ *goal, int travelflags, float lookahead, vec3_t target); int trap_BotPredictVisiblePosition(vec3_t origin, int areanum, void /* struct bot_goal_s */ *goal, int travelflags, vec3_t target); int trap_BotAllocMoveState(void); void trap_BotFreeMoveState(int handle); void trap_BotInitMoveState(int handle, void /* struct bot_initmove_s */ *initmove); void trap_BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type); int trap_BotChooseBestFightWeapon(int weaponstate, int *inventory); void trap_BotGetWeaponInfo(int weaponstate, int weapon, void /* struct weaponinfo_s */ *weaponinfo); int trap_BotLoadWeaponWeights(int weaponstate, char *filename); int trap_BotAllocWeaponState(void); void trap_BotFreeWeaponState(int weaponstate); void trap_BotResetWeaponState(int weaponstate); int trap_GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); void trap_SnapVector( float *v ); //KK-OAX //These enable the simplified command handling. #define CMD_CHEAT 0x0001 #define CMD_CHEAT_TEAM 0x0002 // is a cheat when used on a team #define CMD_MESSAGE 0x0004 // sends message to others (skip when muted) #define CMD_TEAM 0x0008 // must be on a team #define CMD_NOTEAM 0x0010 // must not be on a team #define CMD_RED 0x0020 // must be on the red team (useless right now) #define CMD_BLUE 0x0040 // must be on the blue team (useless right now) #define CMD_LIVING 0x0080 #define CMD_INTERMISSION 0x0100 // valid during intermission typedef struct { char *cmdName; int cmdFlags; void ( *cmdHandler )( gentity_t *ent ); } commands_t; // // g_svcmds_ext.c // These were added to a seperate file to keep g_svcmds.c navigable. void Svcmd_Status_f( void ); void Svcmd_TeamMessage_f( void ); void Svcmd_CenterPrint_f( void ); void Svcmd_BannerPrint_f( void ); void Svcmd_EjectClient_f( void ); void Svcmd_DumpUser_f( void ); void Svcmd_Chat_f( void ); void Svcmd_ListIP_f( void ); void Svcmd_MessageWrapper( void ); #include "g_killspree.h" #include "g_admin.h" openarena_0.8.8.orig/code/game/ai_dmnet.c0000644000175000017500000024175211656310264017010 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmnet.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_dmnet.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" //data file headers #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "../../ui/menudef.h" //goal flag, see ../botlib/be_ai_goal.h for the other GFL_* #define GFL_AIR 128 int numnodeswitches; char nodeswitch[MAX_NODESWITCHES+1][144]; #define LOOKAHEAD_DISTANCE 300 extern bot_goal_t dom_points_bot[MAX_DOMINATION_POINTS]; /* ================== BotResetNodeSwitches ================== */ void BotResetNodeSwitches(void) { numnodeswitches = 0; } /* ================== BotDumpNodeSwitches ================== */ void BotDumpNodeSwitches(bot_state_t *bs) { int i; char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); BotAI_Print(PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, FloatTime(), MAX_NODESWITCHES); for (i = 0; i < numnodeswitches; i++) { BotAI_Print(PRT_MESSAGE, "%s", nodeswitch[i]); } BotAI_Print(PRT_FATAL, ""); } /* ================== BotRecordNodeSwitch ================== */ void BotRecordNodeSwitch(bot_state_t *bs, char *node, char *str, char *s) { char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); Com_sprintf(nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s from %s\n", netname, FloatTime(), node, str, s); #ifdef DEBUG if (0) { BotAI_Print(PRT_MESSAGE, "%s", nodeswitch[numnodeswitches]); } #endif //DEBUG numnodeswitches++; } /* ================== BotGetAirGoal ================== */ int BotGetAirGoal(bot_state_t *bs, bot_goal_t *goal) { bsp_trace_t bsptrace; vec3_t end, mins = {-15, -15, -2}, maxs = {15, 15, 2}; int areanum; //trace up until we hit solid VectorCopy(bs->origin, end); end[2] += 1000; BotAI_Trace(&bsptrace, bs->origin, mins, maxs, end, bs->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); //trace down until we hit water VectorCopy(bsptrace.endpos, end); BotAI_Trace(&bsptrace, end, mins, maxs, bs->origin, bs->entitynum, CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA); //if we found the water surface if (bsptrace.fraction > 0) { areanum = BotPointAreaNum(bsptrace.endpos); if (areanum) { VectorCopy(bsptrace.endpos, goal->origin); goal->origin[2] -= 2; goal->areanum = areanum; goal->mins[0] = -15; goal->mins[1] = -15; goal->mins[2] = -1; goal->maxs[0] = 15; goal->maxs[1] = 15; goal->maxs[2] = 1; goal->flags = GFL_AIR; goal->number = 0; goal->iteminfo = 0; goal->entitynum = 0; return qtrue; } } return qfalse; } /* ================== BotGoForAir ================== */ int BotGoForAir(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) { bot_goal_t goal; //if the bot needs air if (bs->lastair_time < FloatTime() - 6) { // #ifdef DEBUG //BotAI_Print(PRT_MESSAGE, "going for air\n"); #endif //DEBUG //if we can find an air goal if (BotGetAirGoal(bs, &goal)) { trap_BotPushGoal(bs->gs, &goal); return qtrue; } else { //get a nearby goal outside the water while(trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range)) { trap_BotGetTopGoal(bs->gs, &goal); //if the goal is not in water if (!(trap_AAS_PointContents(goal.origin) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) { return qtrue; } trap_BotPopGoal(bs->gs); } trap_BotResetAvoidGoals(bs->gs); } } return qfalse; } /* ================== BotNearbyGoal ================== */ int BotNearbyGoal(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) { int ret; //check if the bot should go for air if (BotGoForAir(bs, tfl, ltg, range)) return qtrue; // if the bot is carrying a flag or cubes if (BotCTFCarryingFlag(bs) #ifdef MISSIONPACK || Bot1FCTFCarryingFlag(bs) || BotHarvesterCarryingCubes(bs) #endif ) { //if the bot is just a few secs away from the base if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->teamgoal.areanum, TFL_DEFAULT) < 300) { //make the range really small range = 50; } } // ret = trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range); /* if (ret) { char buf[128]; //get the goal at the top of the stack trap_BotGetTopGoal(bs->gs, &goal); trap_BotGoalName(goal.number, buf, sizeof(buf)); BotAI_Print(PRT_MESSAGE, "%1.1f: new nearby goal %s\n", FloatTime(), buf); } */ return ret; } /* ================== BotReachedGoal ================== */ int BotReachedGoal(bot_state_t *bs, bot_goal_t *goal) { if (goal->flags & GFL_ITEM) { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) { if (!(goal->flags & GFL_DROPPED)) { trap_BotSetAvoidGoalTime(bs->gs, goal->number, -1); } return qtrue; } //if the goal isn't there if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) { /* float avoidtime; int t; avoidtime = trap_BotAvoidGoalTime(bs->gs, goal->number); if (avoidtime > 0) { t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal->areanum, bs->tfl); if ((float) t * 0.009 < avoidtime) return qtrue; } */ return qtrue; } //if in the goal area and below or above the goal and not swimming if (bs->areanum == goal->areanum) { if (bs->origin[0] > goal->origin[0] + goal->mins[0] && bs->origin[0] < goal->origin[0] + goal->maxs[0]) { if (bs->origin[1] > goal->origin[1] + goal->mins[1] && bs->origin[1] < goal->origin[1] + goal->maxs[1]) { if (!trap_AAS_Swimming(bs->origin)) { return qtrue; } } } } } else if (goal->flags & GFL_AIR) { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue; //if the bot got air if (bs->lastair_time > FloatTime() - 1) return qtrue; } else { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue; } return qfalse; } /* ================== BotGetItemLongTermGoal ================== */ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { //if the bot has no goal if (!trap_BotGetTopGoal(bs->gs, goal)) { //BotAI_Print(PRT_MESSAGE, "no ltg on stack\n"); bs->ltg_time = 0; } //if the bot touches the current goal else if (BotReachedGoal(bs, goal)) { BotChooseWeapon(bs); bs->ltg_time = 0; } //if it is time to find a new long term goal if (bs->ltg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //BotAI_Print(PRT_MESSAGE, "%s: choosing new ltg\n", ClientName(bs->client, netname, sizeof(netname))); //choose a new goal //BotAI_Print(PRT_MESSAGE, "%6.1f client %d: BotChooseLTGItem\n", FloatTime(), bs->client); if (trap_BotChooseLTGItem(bs->gs, bs->origin, bs->inventory, tfl)) { /* char buf[128]; //get the goal at the top of the stack trap_BotGetTopGoal(bs->gs, goal); trap_BotGoalName(goal->number, buf, sizeof(buf)); BotAI_Print(PRT_MESSAGE, "%1.1f: new long term goal %s\n", FloatTime(), buf); */ bs->ltg_time = FloatTime() + 20; } else {//the bot gets sorta stuck with all the avoid timings, shouldn't happen though // #ifdef DEBUG char netname[128]; BotAI_Print(PRT_MESSAGE, "%s: no valid ltg (probably stuck)\n", ClientName(bs->client, netname, sizeof(netname))); #endif //trap_BotDumpAvoidGoals(bs->gs); //reset the avoid goals and the avoid reach trap_BotResetAvoidGoals(bs->gs); trap_BotResetAvoidReach(bs->ms); } //get the goal at the top of the stack return trap_BotGetTopGoal(bs->gs, goal); } return qtrue; } /* ================== BotGetLongTermGoal we could also create a seperate AI node for every long term goal type however this saves us a lot of code ================== */ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) { vec3_t target, dir, dir2; char netname[MAX_NETNAME]; char buf[MAX_MESSAGE_SIZE]; int areanum; float croucher; aas_entityinfo_t entinfo, botinfo; bot_waypoint_t *wp; if (bs->ltgtype == LTG_TEAMHELP && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "help_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //if trying to help the team mate for more than a minute if (bs->teamgoal_time < FloatTime()) bs->ltgtype = 0; //if the team mate IS visible for quite some time if (bs->teammatevisible_time < FloatTime() - 10) bs->ltgtype = 0; //get entity information of the companion BotEntityInfo(bs->teammate, &entinfo); //if the team mate is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) { //if close just stand still there VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(100)) { trap_BotResetAvoidReach(bs->ms); return qfalse; } } else { //last time the bot was NOT visible bs->teammatevisible_time = FloatTime(); } //if the entity information is valid (entity in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->teamgoal.entitynum = bs->teammate; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); return qtrue; } //if the bot accompanies someone if (bs->ltgtype == LTG_TEAMACCOMPANY && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "accompany_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //if accompanying the companion for 3 minutes if (bs->teamgoal_time < FloatTime()) { BotAI_BotInitialChat(bs, "accompany_stop", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->ltgtype = 0; } //get entity information of the companion BotEntityInfo(bs->teammate, &entinfo); //if the companion is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) { //update visible time bs->teammatevisible_time = FloatTime(); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(bs->formation_dist)) { // // if the client being followed bumps into this bot then // the bot should back up BotEntityInfo(bs->entitynum, &botinfo); // if the followed client is not standing ontop of the bot if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2]) { // if the bounding boxes touch each other if (botinfo.origin[0] + botinfo.maxs[0] > entinfo.origin[0] + entinfo.mins[0] - 4&& botinfo.origin[0] + botinfo.mins[0] < entinfo.origin[0] + entinfo.maxs[0] + 4) { if (botinfo.origin[1] + botinfo.maxs[1] > entinfo.origin[1] + entinfo.mins[1] - 4 && botinfo.origin[1] + botinfo.mins[1] < entinfo.origin[1] + entinfo.maxs[1] + 4) { if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2] - 4 && botinfo.origin[2] + botinfo.mins[2] < entinfo.origin[2] + entinfo.maxs[2] + 4) { // if the followed client looks in the direction of this bot AngleVectors(entinfo.angles, dir, NULL, NULL); dir[2] = 0; VectorNormalize(dir); //VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); VectorSubtract(bs->origin, entinfo.origin, dir2); VectorNormalize(dir2); if (DotProduct(dir, dir2) > 0.7) { // back up BotSetupForMovement(bs); trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK); } } } } } //check if the bot wants to crouch //don't crouch if crouched less than 5 seconds ago if (bs->attackcrouch_time < FloatTime() - 5) { croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); if (random() < bs->thinktime * croucher) { bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; } } //don't crouch when swimming if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1; //if not arrived yet or arived some time ago if (bs->arrive_time < FloatTime() - 2) { //if not arrived yet if (!bs->arrive_time) { trap_EA_Gesture(bs->client); BotAI_BotInitialChat(bs, "accompany_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->arrive_time = FloatTime(); } //if the bot wants to crouch else if (bs->attackcrouch_time > FloatTime()) { trap_EA_Crouch(bs->client); } //else do some model taunts else if (random() < bs->thinktime * 0.05) { //do a gesture :) trap_EA_Gesture(bs->client); } } //if just arrived look at the companion if (bs->arrive_time > FloatTime() - 2) { VectorSubtract(entinfo.origin, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //else look strategically around for enemies else if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //check if the bot wants to go for air if (BotGoForAir(bs, bs->tfl, &bs->teamgoal, 400)) { trap_BotResetLastAvoidReach(bs->ms); //get the goal at the top of the stack //trap_BotGetTopGoal(bs->gs, &tmpgoal); //trap_BotGoalName(tmpgoal.number, buf, 144); //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + 8; AIEnter_Seek_NBG(bs, "BotLongTermGoal: go for air"); return qfalse; } // trap_BotResetAvoidReach(bs->ms); return qfalse; } } //if the entity information is valid (entity in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->teamgoal.entitynum = bs->teammate; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //the goal the bot should go for memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //if the companion is NOT visible for too long if (bs->teammatevisible_time < FloatTime() - 60) { BotAI_BotInitialChat(bs, "accompany_cannotfind", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->ltgtype = 0; // just to make sure the bot won't spam this message bs->teammatevisible_time = FloatTime(); } return qtrue; } // if (bs->ltgtype == LTG_DEFENDKEYAREA) { if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->teamgoal.areanum, TFL_DEFAULT) > bs->defendaway_range) { bs->defendaway_time = 0; } } //For double domination if (bs->ltgtype == LTG_POINTA && bs->defendaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "dd_start_pointa", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); //BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); //if very close... go away for some time VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(70)) { trap_BotResetAvoidReach(bs->ms); bs->defendaway_time = FloatTime() + 3 + 3 * random(); if (BotHasPersistantPowerupAndWeapon(bs)) { bs->defendaway_range = 100; } else { bs->defendaway_range = 350; } } return qtrue; } if (bs->ltgtype == LTG_POINTB && bs->defendaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "dd_start_pointb", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); //BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); //if very close... go away for some time VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(70)) { trap_BotResetAvoidReach(bs->ms); bs->defendaway_time = FloatTime() + 3 + 3 * random(); if (BotHasPersistantPowerupAndWeapon(bs)) { bs->defendaway_range = 100; } else { bs->defendaway_range = 350; } } return qtrue; } //if (bs->ltgtype == LTG_DOMHOLD && // bs->defendaway_time < FloatTime()) { //check for bot typing status message /*if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "dd_start_pointb", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); //BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); bs->teammessage_time = 0; }*/ //set the bot goal // memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //if very close... go away for some time // VectorSubtract(goal->origin, bs->origin, dir); // if (VectorLengthSquared(dir) < Square(30)) { /*trap_BotResetAvoidReach(bs->ms); bs->defendaway_time = FloatTime() + 3 + 3 * random(); if (BotHasPersistantPowerupAndWeapon(bs)) { bs->defendaway_range = 100; } else { bs->defendaway_range = 350; }*/ // memcpy(&bs->teamgoal, &dom_points_bot[((rand()) % (level.domination_points_count))], sizeof(bot_goal_t)); // BotAlternateRoute(bs, &bs->teamgoal); // BotSetTeamStatus(bs); //} //return qtrue; // } //if defending a key area if (bs->ltgtype == LTG_DEFENDKEYAREA && !retreat && bs->defendaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "defend_start", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //stop after 2 minutes if (bs->teamgoal_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "defend_stop", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); bs->ltgtype = 0; } //if very close... go away for some time VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(70)) { trap_BotResetAvoidReach(bs->ms); bs->defendaway_time = FloatTime() + 3 + 3 * random(); if (BotHasPersistantPowerupAndWeapon(bs)) { bs->defendaway_range = 100; } else { bs->defendaway_range = 350; } } return qtrue; } //going to kill someone if (bs->ltgtype == LTG_KILL && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "kill_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->teammessage_time = 0; } // if (bs->lastkilledplayer == bs->teamgoal.entitynum) { EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "kill_done", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->lastkilledplayer = -1; bs->ltgtype = 0; } // if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //just roam around return BotGetItemLongTermGoal(bs, tfl, goal); } //get an item if (bs->ltgtype == LTG_GETITEM && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //stop after some time if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } // if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_notthere", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } else if (BotReachedGoal(bs, goal)) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_gotit", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } return qtrue; } //if camping somewhere if ((bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER) && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); // if (bs->teamgoal_time < FloatTime()) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); } bs->ltgtype = 0; } //if really near the camp spot VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(60)) { //if not arrived yet if (!bs->arrive_time) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_INPOSITION); } bs->arrive_time = FloatTime(); } //look strategically around for enemies if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //check if the bot wants to crouch //don't crouch if crouched less than 5 seconds ago if (bs->attackcrouch_time < FloatTime() - 5) { croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); if (random() < bs->thinktime * croucher) { bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; } } //if the bot wants to crouch if (bs->attackcrouch_time > FloatTime()) { trap_EA_Crouch(bs->client); } //don't crouch when swimming if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1; //make sure the bot is not gonna drown if (trap_PointContents(bs->eye,bs->entitynum) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); // if (bs->lastgoal_ltgtype == LTG_CAMPORDER) { bs->lastgoal_ltgtype = 0; } } bs->ltgtype = 0; } // if (bs->camp_range > 0) { //FIXME: move around a bit } // trap_BotResetAvoidReach(bs->ms); return qfalse; } return qtrue; } //patrolling along several waypoints if (bs->ltgtype == LTG_PATROL && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { strcpy(buf, ""); for (wp = bs->patrolpoints; wp; wp = wp->next) { strcat(buf, wp->name); if (wp->next) strcat(buf, " to "); } BotAI_BotInitialChat(bs, "patrol_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } // if (!bs->curpatrolpoint) { bs->ltgtype = 0; return qfalse; } //if the bot touches the current goal if (trap_BotTouchingGoal(bs->origin, &bs->curpatrolpoint->goal)) { if (bs->patrolflags & PATROL_BACK) { if (bs->curpatrolpoint->prev) { bs->curpatrolpoint = bs->curpatrolpoint->prev; } else { bs->curpatrolpoint = bs->curpatrolpoint->next; bs->patrolflags &= ~PATROL_BACK; } } else { if (bs->curpatrolpoint->next) { bs->curpatrolpoint = bs->curpatrolpoint->next; } else { bs->curpatrolpoint = bs->curpatrolpoint->prev; bs->patrolflags |= PATROL_BACK; } } } //stop after 5 minutes if (bs->teamgoal_time < FloatTime()) { BotAI_BotInitialChat(bs, "patrol_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } if (!bs->curpatrolpoint) { bs->ltgtype = 0; return qfalse; } memcpy(goal, &bs->curpatrolpoint->goal, sizeof(bot_goal_t)); return qtrue; } #ifdef CTF if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //if going for enemy flag if (bs->ltgtype == LTG_GETFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "captureflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG); bs->teammessage_time = 0; } // switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if touching the flag if (trap_BotTouchingGoal(bs->origin, goal)) { // make sure the bot knows the flag isn't there anymore switch(BotTeam(bs)) { case TEAM_RED: bs->blueflagstatus = 1; break; case TEAM_BLUE: bs->redflagstatus = 1; break; } bs->ltgtype = 0; } //stop after 3 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); return qtrue; } //if rushing to the base if (bs->ltgtype == LTG_RUSHBASE && bs->rushbaseaway_time < FloatTime()) { switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if not carrying the flag anymore if (!BotCTFCarryingFlag(bs)) bs->ltgtype = 0; //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) bs->ltgtype = 0; //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { //if the bot is still carrying the enemy flag then the //base flag is gone, now just walk near the base a bit if (BotCTFCarryingFlag(bs)) { trap_BotResetAvoidReach(bs->ms); bs->rushbaseaway_time = FloatTime() + 5 + 10 * random(); //FIXME: add chat to tell the others to get back the flag } else { bs->ltgtype = 0; } } BotAlternateRoute(bs, goal); return qtrue; } //returning flag if (bs->ltgtype == LTG_RETURNFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "returnflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG); bs->teammessage_time = 0; } // switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if touching the flag if (trap_BotTouchingGoal(bs->origin, goal)) bs->ltgtype = 0; //stop after 3 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); return qtrue; } } #endif //CTF else if (gametype == GT_1FCTF) { if (bs->ltgtype == LTG_GETFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "captureflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG); bs->teammessage_time = 0; } memcpy(goal, &ctf_neutralflag, sizeof(bot_goal_t)); //if touching the flag if (trap_BotTouchingGoal(bs->origin, goal)) { bs->ltgtype = 0; } //stop after 3 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } return qtrue; } //if rushing to the base if (bs->ltgtype == LTG_RUSHBASE) { switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if not carrying the flag anymore if (!Bot1FCTFCarryingFlag(bs)) { bs->ltgtype = 0; } //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); return qtrue; } //attack the enemy base if (bs->ltgtype == LTG_ATTACKENEMYBASE && bs->attackaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); bs->teammessage_time = 0; } switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { bs->attackaway_time = FloatTime() + 2 + 5 * random(); } return qtrue; } //returning flag if (bs->ltgtype == LTG_RETURNFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "returnflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG); bs->teammessage_time = 0; } // if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //just roam around return BotGetItemLongTermGoal(bs, tfl, goal); } } else if (gametype == GT_OBELISK) { if (bs->ltgtype == LTG_ATTACKENEMYBASE && bs->attackaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); bs->teammessage_time = 0; } switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if the bot no longer wants to attack the obelisk if (BotFeelingBad(bs) > 50) { return BotGetItemLongTermGoal(bs, tfl, goal); } //if touching the obelisk if (trap_BotTouchingGoal(bs->origin, goal)) { bs->attackaway_time = FloatTime() + 3 + 5 * random(); } // or very close to the obelisk VectorSubtract(bs->origin, goal->origin, dir); if (VectorLengthSquared(dir) < Square(60)) { bs->attackaway_time = FloatTime() + 3 + 5 * random(); } //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); //just move towards the obelisk return qtrue; } } else if (gametype == GT_HARVESTER) { //if rushing to the base if (bs->ltgtype == LTG_RUSHBASE) { switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; default: BotGoHarvest(bs); return qfalse; } //if not carrying any cubes if (!BotHarvesterCarryingCubes(bs)) { BotGoHarvest(bs); return qfalse; } //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) { BotGoHarvest(bs); return qfalse; } //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { BotGoHarvest(bs); return qfalse; } BotAlternateRoute(bs, goal); return qtrue; } //attack the enemy base if (bs->ltgtype == LTG_ATTACKENEMYBASE && bs->attackaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "attackenemybase_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); bs->teammessage_time = 0; } switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &blueobelisk, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &redobelisk, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { bs->attackaway_time = FloatTime() + 2 + 5 * random(); } return qtrue; } //harvest cubes if (bs->ltgtype == LTG_HARVEST && bs->harvestaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "harvest_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONOFFENSE); bs->teammessage_time = 0; } memcpy(goal, &neutralobelisk, sizeof(bot_goal_t)); // if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } // if (trap_BotTouchingGoal(bs->origin, goal)) { bs->harvestaway_time = FloatTime() + 4 + 3 * random(); } return qtrue; } } //#endif //normal goal stuff return BotGetItemLongTermGoal(bs, tfl, goal); } /* ================== BotLongTermGoal ================== */ int BotLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) { aas_entityinfo_t entinfo; char teammate[MAX_MESSAGE_SIZE]; float squaredist; int areanum; vec3_t dir; //FIXME: also have air long term goals? // //if the bot is leading someone and not retreating if (bs->lead_time > 0 && !retreat) { if (bs->lead_time < FloatTime()) { BotAI_BotInitialChat(bs, "lead_stop", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->lead_time = 0; return BotGetLongTermGoal(bs, tfl, retreat, goal); } // if (bs->leadmessage_time < 0 && -bs->leadmessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //get entity information of the companion BotEntityInfo(bs->lead_teammate, &entinfo); // if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->lead_teamgoal.entitynum = bs->lead_teammate; bs->lead_teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->lead_teamgoal.origin); VectorSet(bs->lead_teamgoal.mins, -8, -8, -8); VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8); } } //if the team mate is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->lead_teammate)) { bs->leadvisible_time = FloatTime(); } //if the team mate is not visible for 1 seconds if (bs->leadvisible_time < FloatTime() - 1) { bs->leadbackup_time = FloatTime() + 2; } //distance towards the team mate VectorSubtract(bs->origin, bs->lead_teamgoal.origin, dir); squaredist = VectorLengthSquared(dir); //if backing up towards the team mate if (bs->leadbackup_time > FloatTime()) { if (bs->leadmessage_time < FloatTime() - 20) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //if very close to the team mate if (squaredist < Square(100)) { bs->leadbackup_time = 0; } //the bot should go back to the team mate memcpy(goal, &bs->lead_teamgoal, sizeof(bot_goal_t)); return qtrue; } else { //if quite distant from the team mate if (squaredist > Square(500)) { if (bs->leadmessage_time < FloatTime() - 20) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //look at the team mate VectorSubtract(entinfo.origin, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; //just wait for the team mate return qfalse; } } } return BotGetLongTermGoal(bs, tfl, retreat, goal); } /* ================== AIEnter_Intermission ================== */ void AIEnter_Intermission(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "intermission", "", s); //reset the bot state BotResetState(bs); //check for end level chat if (BotChat_EndLevel(bs)) { trap_BotEnterChat(bs->cs, 0, bs->chatto); } bs->ainode = AINode_Intermission; } /* ================== AINode_Intermission ================== */ int AINode_Intermission(bot_state_t *bs) { //if the intermission ended if (!BotIntermission(bs)) { if (BotChat_StartLevel(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); } else { bs->stand_time = FloatTime() + 2; } AIEnter_Stand(bs, "intermission: chat"); } return qtrue; } /* ================== AIEnter_Observer ================== */ void AIEnter_Observer(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "observer", "", s); //reset the bot state BotResetState(bs); bs->ainode = AINode_Observer; } /* ================== AINode_Observer ================== */ int AINode_Observer(bot_state_t *bs) { //if the bot left observer mode if (!BotIsObserver(bs)) { AIEnter_Stand(bs, "observer: left observer"); } return qtrue; } /* ================== AIEnter_Stand ================== */ void AIEnter_Stand(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "stand", "", s); bs->standfindenemy_time = FloatTime() + 1; bs->ainode = AINode_Stand; } /* ================== AINode_Stand ================== */ int AINode_Stand(bot_state_t *bs) { //if the bot's health decreased if (bs->lastframe_health > bs->inventory[INVENTORY_HEALTH]) { if (BotChat_HitTalking(bs)) { bs->standfindenemy_time = FloatTime() + BotChatTime(bs) + 0.1; bs->stand_time = FloatTime() + BotChatTime(bs) + 0.1; } } if (bs->standfindenemy_time < FloatTime()) { if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "stand: found enemy"); return qfalse; } bs->standfindenemy_time = FloatTime() + 1; } // put up chat icon trap_EA_Talk(bs->client); // when done standing if (bs->stand_time < FloatTime()) { trap_BotEnterChat(bs->cs, 0, bs->chatto); AIEnter_Seek_LTG(bs, "stand: time out"); return qfalse; } // return qtrue; } /* ================== AIEnter_Respawn ================== */ void AIEnter_Respawn(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "respawn", "", s); //reset some states trap_BotResetMoveState(bs->ms); trap_BotResetGoalState(bs->gs); trap_BotResetAvoidGoals(bs->gs); trap_BotResetAvoidReach(bs->ms); //if the bot wants to chat if (BotChat_Death(bs)) { bs->respawn_time = FloatTime() + BotChatTime(bs); bs->respawnchat_time = FloatTime(); } else { bs->respawn_time = FloatTime() + 1 + random(); bs->respawnchat_time = 0; } //set respawn state bs->respawn_wait = qfalse; bs->ainode = AINode_Respawn; } /* ================== AINode_Respawn ================== */ int AINode_Respawn(bot_state_t *bs) { // if waiting for the actual respawn if (bs->respawn_wait) { if (!BotIsDead(bs)) { AIEnter_Seek_LTG(bs, "respawn: respawned"); } else { trap_EA_Respawn(bs->client); } } else if (bs->respawn_time < FloatTime()) { // wait until respawned bs->respawn_wait = qtrue; // elementary action respawn trap_EA_Respawn(bs->client); // if (bs->respawnchat_time) { trap_BotEnterChat(bs->cs, 0, bs->chatto); bs->enemy = -1; } } if (bs->respawnchat_time && bs->respawnchat_time < FloatTime() - 0.5) { trap_EA_Talk(bs->client); } // return qtrue; } /* ================== BotSelectActivateWeapon ================== */ int BotSelectActivateWeapon(bot_state_t *bs) { // if (bs->inventory[INVENTORY_MACHINEGUN] > 0 && bs->inventory[INVENTORY_BULLETS] > 0) return WEAPONINDEX_MACHINEGUN; else if (bs->inventory[INVENTORY_SHOTGUN] > 0 && bs->inventory[INVENTORY_SHELLS] > 0) return WEAPONINDEX_SHOTGUN; else if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) return WEAPONINDEX_PLASMAGUN; else if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 0) return WEAPONINDEX_LIGHTNING; else if (bs->inventory[INVENTORY_CHAINGUN] > 0 && bs->inventory[INVENTORY_BELT] > 0) return WEAPONINDEX_CHAINGUN; else if (bs->inventory[INVENTORY_NAILGUN] > 0 && bs->inventory[INVENTORY_NAILS] > 0) return WEAPONINDEX_NAILGUN; else if (bs->inventory[INVENTORY_PROXLAUNCHER] > 0 && bs->inventory[INVENTORY_MINES] > 0) return WEAPONINDEX_PROXLAUNCHER; else if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 && bs->inventory[INVENTORY_GRENADES] > 0) return WEAPONINDEX_GRENADE_LAUNCHER; else if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 0) return WEAPONINDEX_RAILGUN; else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) return WEAPONINDEX_ROCKET_LAUNCHER; else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) return WEAPONINDEX_BFG; else { return -1; } } /* ================== BotClearPath try to deactivate obstacles like proximity mines on the bot's path ================== */ void BotClearPath(bot_state_t *bs, bot_moveresult_t *moveresult) { int i, bestmine; float dist, bestdist; vec3_t target, dir; bsp_trace_t bsptrace; entityState_t state; // if there is a dead body wearing kamikze nearby if (bs->kamikazebody) { // if the bot's view angles and weapon are not used for movement if ( !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { // BotAI_GetEntityState(bs->kamikazebody, &state); VectorCopy(state.pos.trBase, target); target[2] += 8; VectorSubtract(target, bs->eye, dir); vectoangles(dir, moveresult->ideal_viewangles); // moveresult->weapon = BotSelectActivateWeapon(bs); if (moveresult->weapon == -1) { // FIXME: run away! moveresult->weapon = 0; } if (moveresult->weapon) { // moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; // if holding the right weapon if (bs->cur_ps.weapon == moveresult->weapon) { // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); // if the mine is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { // shoot at the mine trap_EA_Attack(bs->client); } } } } } } if (moveresult->flags & MOVERESULT_BLOCKEDBYAVOIDSPOT) { bs->blockedbyavoidspot_time = FloatTime() + 5; } // if blocked by an avoid spot and the view angles and weapon are used for movement if (bs->blockedbyavoidspot_time > FloatTime() && !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { bestdist = 300; bestmine = -1; for (i = 0; i < bs->numproxmines; i++) { BotAI_GetEntityState(bs->proxmines[i], &state); VectorSubtract(state.pos.trBase, bs->origin, dir); dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestmine = i; } } if (bestmine != -1) { // // state->generic1 == TEAM_RED || state->generic1 == TEAM_BLUE // // deactivate prox mines in the bot's path by shooting // rockets or plasma cells etc. at them BotAI_GetEntityState(bs->proxmines[bestmine], &state); VectorCopy(state.pos.trBase, target); target[2] += 2; VectorSubtract(target, bs->eye, dir); vectoangles(dir, moveresult->ideal_viewangles); // if the bot has a weapon that does splash damage if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) moveresult->weapon = WEAPONINDEX_PLASMAGUN; else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) moveresult->weapon = WEAPONINDEX_ROCKET_LAUNCHER; else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) moveresult->weapon = WEAPONINDEX_BFG; else { moveresult->weapon = 0; } if (moveresult->weapon) { // moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; // if holding the right weapon if (bs->cur_ps.weapon == moveresult->weapon) { // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); // if the mine is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { // shoot at the mine trap_EA_Attack(bs->client); } } } } } } } /* ================== AIEnter_Seek_ActivateEntity ================== */ void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "activate entity", "", s); bs->ainode = AINode_Seek_ActivateEntity; } /* ================== AINode_Seek_Activate_Entity ================== */ int AINode_Seek_ActivateEntity(bot_state_t *bs) { bot_goal_t *goal; vec3_t target, dir, ideal_viewangles; bot_moveresult_t moveresult; int targetvisible; bsp_trace_t bsptrace; aas_entityinfo_t entinfo; if (BotIsObserver(bs)) { BotClearActivateGoalStack(bs); AIEnter_Observer(bs, "active entity: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { BotClearActivateGoalStack(bs); AIEnter_Intermission(bs, "activate entity: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { BotClearActivateGoalStack(bs); AIEnter_Respawn(bs, "activate entity: bot dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; // if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // map specific code BotMapScripts(bs); // no enemy bs->enemy = -1; // if the bot has no activate goal if (!bs->activatestack) { BotClearActivateGoalStack(bs); AIEnter_Seek_NBG(bs, "activate entity: no goal"); return qfalse; } // goal = &bs->activatestack->goal; // initialize target being visible to false targetvisible = qfalse; // if the bot has to shoot at a target to activate something if (bs->activatestack->shoot) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->activatestack->target, bs->entitynum, MASK_SHOT); // if the shootable entity is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == goal->entitynum) { targetvisible = qtrue; // if holding the right weapon if (bs->cur_ps.weapon == bs->activatestack->weapon) { VectorSubtract(bs->activatestack->target, bs->eye, dir); vectoangles(dir, ideal_viewangles); // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, ideal_viewangles)) { trap_EA_Attack(bs->client); } } } } // if the shoot target is visible if (targetvisible) { // get the entity info of the entity the bot is shooting at BotEntityInfo(goal->entitynum, &entinfo); // if the entity the bot shoots at moved if (!VectorCompare(bs->activatestack->origin, entinfo.origin)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "hit shootable button or trigger\n"); #endif //DEBUG bs->activatestack->time = 0; } // if the activate goal has been activated or the bot takes too long if (bs->activatestack->time < FloatTime()) { BotPopFromActivateGoalStack(bs); // if there are more activate goals on the stack if (bs->activatestack) { bs->activatestack->time = FloatTime() + 10; return qfalse; } AIEnter_Seek_NBG(bs, "activate entity: time out"); return qfalse; } memset(&moveresult, 0, sizeof(bot_moveresult_t)); } else { // if the bot has no goal if (!goal) { bs->activatestack->time = 0; } // if the bot does not have a shoot goal else if (!bs->activatestack->shoot) { //if the bot touches the current goal if (trap_BotTouchingGoal(bs->origin, goal)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "touched button or trigger\n"); #endif //DEBUG bs->activatestack->time = 0; } } // if the activate goal has been activated or the bot takes too long if (bs->activatestack->time < FloatTime()) { BotPopFromActivateGoalStack(bs); // if there are more activate goals on the stack if (bs->activatestack) { bs->activatestack->time = FloatTime() + 10; return qfalse; } AIEnter_Seek_NBG(bs, "activate entity: activated"); return qfalse; } //predict obstacles if (BotAIPredictObstacles(bs, goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); // bs->activatestack->time = 0; } //check if the bot is blocked BotAIBlocked(bs, &moveresult, qtrue); } // BotClearPath(bs, &moveresult); // if the bot has to shoot to activate if (bs->activatestack->shoot) { // if the view angles aren't yet used for the movement if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEW)) { VectorSubtract(bs->activatestack->target, bs->eye, dir); vectoangles(dir, moveresult.ideal_viewangles); moveresult.flags |= MOVERESULT_MOVEMENTVIEW; } // if there's no weapon yet used for the movement if (!(moveresult.flags & MOVERESULT_MOVEMENTWEAPON)) { moveresult.flags |= MOVERESULT_MOVEMENTWEAPON; // bs->activatestack->weapon = BotSelectActivateWeapon(bs); if (bs->activatestack->weapon == -1) { //FIXME: find a decent weapon first bs->activatestack->weapon = 0; } moveresult.weapon = bs->activatestack->weapon; } } // if the ideal view angles are set for movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } // if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (trap_BotMovementViewTarget(bs->ms, goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } // if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; // if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_NBG(bs, "activate entity: found enemy"); } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "activate entity: found enemy"); } BotClearActivateGoalStack(bs); } return qtrue; } /* ================== AIEnter_Seek_NBG ================== */ void AIEnter_Seek_NBG(bot_state_t *bs, char *s) { bot_goal_t goal; char buf[144]; if (trap_BotGetTopGoal(bs->gs, &goal)) { trap_BotGoalName(goal.number, buf, 144); BotRecordNodeSwitch(bs, "seek NBG", buf, s); } else { BotRecordNodeSwitch(bs, "seek NBG", "no goal", s); } bs->ainode = AINode_Seek_NBG; } /* ================== AINode_Seek_NBG ================== */ int AINode_Seek_NBG(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek nbg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "seek nbg: intermision"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "seek nbg: bot dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //no enemy bs->enemy = -1; //if the bot has no goal if (!trap_BotGetTopGoal(bs->gs, &goal)) bs->nbg_time = 0; //if the bot touches the current goal else if (BotReachedGoal(bs, &goal)) { BotChooseWeapon(bs); bs->nbg_time = 0; } // if (bs->nbg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //check for new nearby items right away //NOTE: we canNOT reset the check_time to zero because it would create an endless loop of node switches bs->check_time = FloatTime() + 0.05; //go back to seek ltg AIEnter_Seek_LTG(bs, "seek nbg: time out"); return qfalse; } //predict obstacles if (BotAIPredictObstacles(bs, &goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); bs->nbg_time = 0; } //check if the bot is blocked BotAIBlocked(bs, &moveresult, qtrue); // BotClearPath(bs, &moveresult); //if the viewangles are used for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } //if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (!trap_BotGetSecondGoal(bs->gs, &goal)) trap_BotGetTopGoal(bs->gs, &goal); if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } //FIXME: look at cluster portals? else vectoangles(moveresult.movedir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_NBG(bs, "seek nbg: found enemy"); } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "seek nbg: found enemy"); } } return qtrue; } /* ================== AIEnter_Seek_LTG ================== */ void AIEnter_Seek_LTG(bot_state_t *bs, char *s) { bot_goal_t goal; char buf[144]; if (trap_BotGetTopGoal(bs->gs, &goal)) { trap_BotGoalName(goal.number, buf, 144); BotRecordNodeSwitch(bs, "seek LTG", buf, s); } else { BotRecordNodeSwitch(bs, "seek LTG", "no goal", s); } bs->ainode = AINode_Seek_LTG; } /* ================== AINode_Seek_LTG ================== */ int AINode_Seek_LTG(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; int range; //char buf[128]; //bot_goal_t tmpgoal; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek ltg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "seek ltg: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "seek ltg: bot dead"); return qfalse; } // if (BotChat_Random(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "seek ltg: random chat"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //no enemy bs->enemy = -1; // if (bs->killedenemy_time > FloatTime() - 2) { if (random() < bs->thinktime * 1) { trap_EA_Gesture(bs->client); } } //if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_Retreat(bs, "seek ltg: found enemy"); return qfalse; } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "seek ltg: found enemy"); return qfalse; } } // BotTeamGoals(bs, qfalse); //get the current long term goal if (!BotLongTermGoal(bs, bs->tfl, qfalse, &goal)) { return qtrue; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 0.5; //check if the bot wants to camp BotWantsToCamp(bs); // if (bs->ltgtype == LTG_DEFENDKEYAREA) range = 400; else range = 150; // #ifdef CTF if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //if carrying a flag the bot shouldn't be distracted too much if (BotCTFCarryingFlag(bs)) range = 50; } #endif //CTF else if (gametype == GT_1FCTF) { if (Bot1FCTFCarryingFlag(bs)) range = 50; } else if (gametype == GT_HARVESTER) { if (BotHarvesterCarryingCubes(bs)) range = 80; } // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { trap_BotResetLastAvoidReach(bs->ms); //get the goal at the top of the stack //trap_BotGetTopGoal(bs->gs, &tmpgoal); //trap_BotGoalName(tmpgoal.number, buf, 144); //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + 4 + range * 0.01; AIEnter_Seek_NBG(bs, "ltg seek: nbg"); return qfalse; } } //predict obstacles if (BotAIPredictObstacles(bs, &goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qtrue); // BotClearPath(bs, &moveresult); //if the viewangles are used for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } //if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } //FIXME: look at cluster portals? else if (VectorLengthSquared(moveresult.movedir)) { vectoangles(moveresult.movedir, bs->ideal_viewangles); } else if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; // return qtrue; } /* ================== AIEnter_Battle_Fight ================== */ void AIEnter_Battle_Fight(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle fight", "", s); trap_BotResetLastAvoidReach(bs->ms); bs->ainode = AINode_Battle_Fight; bs->flags &= ~BFL_FIGHTSUICIDAL; } /* ================== AIEnter_Battle_SuicidalFight ================== */ void AIEnter_Battle_SuicidalFight(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle fight", "", s); trap_BotResetLastAvoidReach(bs->ms); bs->ainode = AINode_Battle_Fight; bs->flags |= BFL_FIGHTSUICIDAL; } /* ================== AINode_Battle_Fight ================== */ int AINode_Battle_Fight(bot_state_t *bs) { int areanum; vec3_t target; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle fight: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle fight: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle fight: bot dead"); return qfalse; } //if there is another better enemy if (BotFindEnemy(bs, bs->enemy)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); #endif } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle fight: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); //if the enemy is dead if (bs->enemydeath_time) { if (bs->enemydeath_time < FloatTime() - 1.0) { bs->enemydeath_time = 0; if (bs->enemysuicide) { BotChat_EnemySuicide(bs); } if (bs->lastkilledplayer == bs->enemy && BotChat_Kill(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: enemy dead"); } else { bs->ltg_time = 0; AIEnter_Seek_LTG(bs, "battle fight: enemy dead"); } return qfalse; } } else { if (EntityIsDead(&entinfo)) { bs->enemydeath_time = FloatTime(); } } //if the enemy is invisible and not shooting the bot looses track easily if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { if (random() < 0.2) { AIEnter_Seek_LTG(bs, "battle fight: invisible"); return qfalse; } } // VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { // if attacking an obelisk if ( bs->enemy == redobelisk.entitynum || bs->enemy == blueobelisk.entitynum ) { target[2] += 16; } } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //if the bot's health decreased if (bs->lastframe_health > bs->inventory[INVENTORY_HEALTH]) { if (BotChat_HitNoDeath(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: chat health decreased"); return qfalse; } } //if the bot hit someone if (bs->cur_ps.persistant[PERS_HITS] > bs->lasthitcount) { if (BotChat_HitNoKill(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: chat hit someone"); return qfalse; } } //if the enemy is not visible if (!BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { #ifdef MISSIONPACK if (bs->enemy == redobelisk.entitynum || bs->enemy == blueobelisk.entitynum) { AIEnter_Battle_Chase(bs, "battle fight: obelisk out of sight"); return qfalse; } #endif if (BotWantsToChase(bs)) { AIEnter_Battle_Chase(bs, "battle fight: enemy out of sight"); return qfalse; } else { AIEnter_Seek_LTG(bs, "battle fight: enemy out of sight"); return qfalse; } } //use holdable items BotBattleUseItems(bs); // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //choose the best weapon to fight with BotChooseWeapon(bs); //do attack movements moveresult = BotAttackMove(bs, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //aim at the enemy BotAimAtEnemy(bs); //attack the enemy if possible BotCheckAttack(bs); //if the bot wants to retreat if (!(bs->flags & BFL_FIGHTSUICIDAL)) { if (BotWantsToRetreat(bs)) { AIEnter_Battle_Retreat(bs, "battle fight: wants to retreat"); return qtrue; } } return qtrue; } /* ================== AIEnter_Battle_Chase ================== */ void AIEnter_Battle_Chase(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle chase", "", s); bs->chase_time = FloatTime(); bs->ainode = AINode_Battle_Chase; } /* ================== AINode_Battle_Chase ================== */ int AINode_Battle_Chase(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; float range; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle chase: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle chase: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle chase: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle chase: no enemy"); return qfalse; } //if the enemy is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { AIEnter_Battle_Fight(bs, "battle chase"); return qfalse; } //if there is another enemy if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "battle chase: better enemy"); return qfalse; } //there is no last enemy area if (!bs->lastenemyareanum) { AIEnter_Seek_LTG(bs, "battle chase: no enemy area"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //create the chase goal goal.entitynum = bs->enemy; goal.areanum = bs->lastenemyareanum; VectorCopy(bs->lastenemyorigin, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); //if the last seen enemy spot is reached the enemy could not be found if (trap_BotTouchingGoal(bs->origin, &goal)) bs->chase_time = 0; //if there's no chase time left if (!bs->chase_time || bs->chase_time < FloatTime() - 10) { AIEnter_Seek_LTG(bs, "battle chase: time out"); return qfalse; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 1; range = 150; // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { //the bot gets 5 seconds to pick up the nearby goal item bs->nbg_time = FloatTime() + 0.1 * range + 1; trap_BotResetLastAvoidReach(bs->ms); AIEnter_Battle_NBG(bs, "battle chase: nbg"); return qfalse; } } // BotUpdateBattleInventory(bs, bs->enemy); //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); // if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (bs->chase_time > FloatTime() - 2) { BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } } bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //if the bot is in the area the enemy was last seen in if (bs->areanum == bs->lastenemyareanum) bs->chase_time = 0; //if the bot wants to retreat (the bot could have been damage during the chase) if (BotWantsToRetreat(bs)) { AIEnter_Battle_Retreat(bs, "battle chase: wants to retreat"); return qtrue; } return qtrue; } /* ================== AIEnter_Battle_Retreat ================== */ void AIEnter_Battle_Retreat(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle retreat", "", s); bs->ainode = AINode_Battle_Retreat; } /* ================== AINode_Battle_Retreat ================== */ int AINode_Battle_Retreat(bot_state_t *bs) { bot_goal_t goal; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; vec3_t target, dir; float attack_skill, range; int areanum; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle retreat: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle retreat: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle retreat: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle retreat: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsDead(&entinfo)) { AIEnter_Seek_LTG(bs, "battle retreat: enemy dead"); return qfalse; } //if there is another better enemy if (BotFindEnemy(bs, bs->enemy)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); #endif } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; //map specific code BotMapScripts(bs); //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //if the bot doesn't want to retreat anymore... probably picked up some nice items if (BotWantsToChase(bs)) { //empty the goal stack, when chasing, only the enemy is the goal trap_BotEmptyGoalStack(bs->gs); //go chase the enemy AIEnter_Battle_Chase(bs, "battle retreat: wants to chase"); return qfalse; } //update the last time the enemy was visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { bs->enemyvisible_time = FloatTime(); VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { // if attacking an obelisk if ( bs->enemy == redobelisk.entitynum || bs->enemy == blueobelisk.entitynum ) { target[2] += 16; } } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } } //if the enemy is NOT visible for 4 seconds if (bs->enemyvisible_time < FloatTime() - 4) { AIEnter_Seek_LTG(bs, "battle retreat: lost enemy"); return qfalse; } //else if the enemy is NOT visible else if (bs->enemyvisible_time < FloatTime()) { //if there is another enemy if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "battle retreat: another enemy"); return qfalse; } } // BotTeamGoals(bs, qtrue); //use holdable items BotBattleUseItems(bs); //get the current long term goal while retreating if (!BotLongTermGoal(bs, bs->tfl, qtrue, &goal)) { AIEnter_Battle_SuicidalFight(bs, "battle retreat: no way out"); return qfalse; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 1; range = 150; #ifdef CTF if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { //if carrying a flag the bot shouldn't be distracted too much if (BotCTFCarryingFlag(bs)) range = 50; } #endif //CTF else if (gametype == GT_1FCTF) { if (Bot1FCTFCarryingFlag(bs)) range = 50; } else if (gametype == GT_HARVESTER) { if (BotHarvesterCarryingCubes(bs)) range = 80; } // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { trap_BotResetLastAvoidReach(bs->ms); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + range / 100 + 1; AIEnter_Battle_NBG(bs, "battle retreat: nbg"); return qfalse; } } //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //choose the best weapon to fight with BotChooseWeapon(bs); //if the view is fixed for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) && !(bs->flags & BFL_IDEALVIEWSET) ) { attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); //if the bot is skilled anough if (attack_skill > 0.3) { BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //attack the enemy if possible BotCheckAttack(bs); // return qtrue; } /* ================== AIEnter_Battle_NBG ================== */ void AIEnter_Battle_NBG(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle NBG", "", s); bs->ainode = AINode_Battle_NBG; } /* ================== AINode_Battle_NBG ================== */ int AINode_Battle_NBG(bot_state_t *bs) { int areanum; bot_goal_t goal; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; float attack_skill; vec3_t target, dir; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle nbg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle nbg: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle nbg: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_NBG(bs, "battle nbg: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsDead(&entinfo)) { AIEnter_Seek_NBG(bs, "battle nbg: enemy dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //update the last time the enemy was visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { bs->enemyvisible_time = FloatTime(); VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { // if attacking an obelisk if ( bs->enemy == redobelisk.entitynum || bs->enemy == blueobelisk.entitynum ) { target[2] += 16; } } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } } //if the bot has no goal or touches the current goal if (!trap_BotGetTopGoal(bs->gs, &goal)) { bs->nbg_time = 0; } else if (BotReachedGoal(bs, &goal)) { bs->nbg_time = 0; } // if (bs->nbg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //if the bot still has a goal if (trap_BotGetTopGoal(bs->gs, &goal)) AIEnter_Battle_Retreat(bs, "battle nbg: time out"); else AIEnter_Battle_Fight(bs, "battle nbg: time out"); // return qfalse; } //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->nbg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //choose the best weapon to fight with BotChooseWeapon(bs); //if the view is fixed for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) && !(bs->flags & BFL_IDEALVIEWSET)) { attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); //if the bot is skilled anough and the enemy is visible if (attack_skill > 0.3) { //&& BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy) BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //attack the enemy if possible BotCheckAttack(bs); // return qtrue; } openarena_0.8.8.orig/code/game/bg_slidemove.c0000644000175000017500000002121711656310264017657 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_slidemove.c -- part of bg_pmove functionality #include "../qcommon/q_shared.h" #include "bg_public.h" #include "bg_local.h" /* input: origin, velocity, bounds, groundPlane, trace function output: origin, velocity, impacts, stairup boolean */ /* ================== PM_SlideMove Returns qtrue if the velocity was clipped in some way ================== */ #define MAX_CLIP_PLANES 5 qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // for ( i = 0 ; i < numplanes ; i++ ) { if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) { VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return ( bumpcount != 0 ); } /* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } VectorCopy(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); } if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } #if 0 // if the down trace can trace back to the original position directly, don't step pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { // use the original move VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); if ( pm->debugLevel ) { Com_Printf("%i:bend\n", c_pmove); } } else #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } } openarena_0.8.8.orig/code/game/g_rankings.h0000644000175000017500000003255411656310264017355 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // g_rankings.h -- score keys for global rankings #ifndef _G_RANKINGS_H_ #define _G_RANKINGS_H_ /* ============================================================================== Key digits: 10^9: report type 1 = normal 2 = developer-only 10^8: stat type 0 = match stat 1 = single player stat 2 = duel stat 10^7: data type 0 = string 1 = uint32 10^6: calculation 0 = use raw value 1 = add to total 2 = average 3 = max 4 = min 10^5 10^4: category 00 = general 01 = session 02 = weapon 03 = ammo 04 = health 05 = armor 06 = powerup 07 = holdable 08 = hazard 09 = reward 10 = teammate 11 = ctf 10^3: 10^2: sub-category 10^1: 10^0: ordinal ============================================================================== */ // general keys #define QGR_KEY_MATCH_RATING 1112000001 #define QGR_KEY_PLAYED_WITH 1210000002 // session keys #define QGR_KEY_HOSTNAME 1000010000 #define QGR_KEY_MAP 1000010001 #define QGR_KEY_MOD 1000010002 #define QGR_KEY_GAMETYPE 1010010003 #define QGR_KEY_FRAGLIMIT 1010010004 #define QGR_KEY_TIMELIMIT 1010010005 #define QGR_KEY_MAXCLIENTS 1010010006 #define QGR_KEY_MAXRATE 1010010007 #define QGR_KEY_MINPING 1010010008 #define QGR_KEY_MAXPING 1010010009 #define QGR_KEY_DEDICATED 1010010010 #define QGR_KEY_VERSION 1000010011 // weapon keys #define QGR_KEY_FRAG 1211020000 #define QGR_KEY_SUICIDE 1111020001 #define QGR_KEY_SHOT_FIRED 1111020002 #define QGR_KEY_HIT_GIVEN 1111020003 #define QGR_KEY_HIT_TAKEN 1111020004 #define QGR_KEY_DAMAGE_GIVEN 1111020005 #define QGR_KEY_DAMAGE_TAKEN 1111020006 #define QGR_KEY_SPLASH_GIVEN 1111020007 #define QGR_KEY_SPLASH_TAKEN 1111020008 #define QGR_KEY_PICKUP_WEAPON 1111020009 #define QGR_KEY_TIME 1111020010 #define QGR_KEY_FRAG_GAUNTLET 1211020100 #define QGR_KEY_SUICIDE_GAUNTLET 1111020101 #define QGR_KEY_SHOT_FIRED_GAUNTLET 1111020102 #define QGR_KEY_HIT_GIVEN_GAUNTLET 1111020103 #define QGR_KEY_HIT_TAKEN_GAUNTLET 1111020104 #define QGR_KEY_DAMAGE_GIVEN_GAUNTLET 1111020105 #define QGR_KEY_DAMAGE_TAKEN_GAUNTLET 1111020106 #define QGR_KEY_SPLASH_GIVEN_GAUNTLET 1111020107 #define QGR_KEY_SPLASH_TAKEN_GAUNTLET 1111020108 #define QGR_KEY_PICKUP_GAUNTLET 1111020109 #define QGR_KEY_TIME_GAUNTLET 1111020110 #define QGR_KEY_FRAG_MACHINEGUN 1211020200 #define QGR_KEY_SUICIDE_MACHINEGUN 1111020201 #define QGR_KEY_SHOT_FIRED_MACHINEGUN 1111020202 #define QGR_KEY_HIT_GIVEN_MACHINEGUN 1111020203 #define QGR_KEY_HIT_TAKEN_MACHINEGUN 1111020204 #define QGR_KEY_DAMAGE_GIVEN_MACHINEGUN 1111020205 #define QGR_KEY_DAMAGE_TAKEN_MACHINEGUN 1111020206 #define QGR_KEY_SPLASH_GIVEN_MACHINEGUN 1111020207 #define QGR_KEY_SPLASH_TAKEN_MACHINEGUN 1111020208 #define QGR_KEY_PICKUP_MACHINEGUN 1111020209 #define QGR_KEY_TIME_MACHINEGUN 1111020210 #define QGR_KEY_FRAG_SHOTGUN 1211020300 #define QGR_KEY_SUICIDE_SHOTGUN 1111020301 #define QGR_KEY_SHOT_FIRED_SHOTGUN 1111020302 #define QGR_KEY_HIT_GIVEN_SHOTGUN 1111020303 #define QGR_KEY_HIT_TAKEN_SHOTGUN 1111020304 #define QGR_KEY_DAMAGE_GIVEN_SHOTGUN 1111020305 #define QGR_KEY_DAMAGE_TAKEN_SHOTGUN 1111020306 #define QGR_KEY_SPLASH_GIVEN_SHOTGUN 1111020307 #define QGR_KEY_SPLASH_TAKEN_SHOTGUN 1111020308 #define QGR_KEY_PICKUP_SHOTGUN 1111020309 #define QGR_KEY_TIME_SHOTGUN 1111020310 #define QGR_KEY_FRAG_GRENADE 1211020400 #define QGR_KEY_SUICIDE_GRENADE 1111020401 #define QGR_KEY_SHOT_FIRED_GRENADE 1111020402 #define QGR_KEY_HIT_GIVEN_GRENADE 1111020403 #define QGR_KEY_HIT_TAKEN_GRENADE 1111020404 #define QGR_KEY_DAMAGE_GIVEN_GRENADE 1111020405 #define QGR_KEY_DAMAGE_TAKEN_GRENADE 1111020406 #define QGR_KEY_SPLASH_GIVEN_GRENADE 1111020407 #define QGR_KEY_SPLASH_TAKEN_GRENADE 1111020408 #define QGR_KEY_PICKUP_GRENADE 1111020409 #define QGR_KEY_TIME_GRENADE 1111020410 #define QGR_KEY_FRAG_ROCKET 1211020500 #define QGR_KEY_SUICIDE_ROCKET 1111020501 #define QGR_KEY_SHOT_FIRED_ROCKET 1111020502 #define QGR_KEY_HIT_GIVEN_ROCKET 1111020503 #define QGR_KEY_HIT_TAKEN_ROCKET 1111020504 #define QGR_KEY_DAMAGE_GIVEN_ROCKET 1111020505 #define QGR_KEY_DAMAGE_TAKEN_ROCKET 1111020506 #define QGR_KEY_SPLASH_GIVEN_ROCKET 1111020507 #define QGR_KEY_SPLASH_TAKEN_ROCKET 1111020508 #define QGR_KEY_PICKUP_ROCKET 1111020509 #define QGR_KEY_TIME_ROCKET 1111020510 #define QGR_KEY_FRAG_PLASMA 1211020600 #define QGR_KEY_SUICIDE_PLASMA 1111020601 #define QGR_KEY_SHOT_FIRED_PLASMA 1111020602 #define QGR_KEY_HIT_GIVEN_PLASMA 1111020603 #define QGR_KEY_HIT_TAKEN_PLASMA 1111020604 #define QGR_KEY_DAMAGE_GIVEN_PLASMA 1111020605 #define QGR_KEY_DAMAGE_TAKEN_PLASMA 1111020606 #define QGR_KEY_SPLASH_GIVEN_PLASMA 1111020607 #define QGR_KEY_SPLASH_TAKEN_PLASMA 1111020608 #define QGR_KEY_PICKUP_PLASMA 1111020609 #define QGR_KEY_TIME_PLASMA 1111020610 #define QGR_KEY_FRAG_RAILGUN 1211020700 #define QGR_KEY_SUICIDE_RAILGUN 1111020701 #define QGR_KEY_SHOT_FIRED_RAILGUN 1111020702 #define QGR_KEY_HIT_GIVEN_RAILGUN 1111020703 #define QGR_KEY_HIT_TAKEN_RAILGUN 1111020704 #define QGR_KEY_DAMAGE_GIVEN_RAILGUN 1111020705 #define QGR_KEY_DAMAGE_TAKEN_RAILGUN 1111020706 #define QGR_KEY_SPLASH_GIVEN_RAILGUN 1111020707 #define QGR_KEY_SPLASH_TAKEN_RAILGUN 1111020708 #define QGR_KEY_PICKUP_RAILGUN 1111020709 #define QGR_KEY_TIME_RAILGUN 1111020710 #define QGR_KEY_FRAG_LIGHTNING 1211020800 #define QGR_KEY_SUICIDE_LIGHTNING 1111020801 #define QGR_KEY_SHOT_FIRED_LIGHTNING 1111020802 #define QGR_KEY_HIT_GIVEN_LIGHTNING 1111020803 #define QGR_KEY_HIT_TAKEN_LIGHTNING 1111020804 #define QGR_KEY_DAMAGE_GIVEN_LIGHTNING 1111020805 #define QGR_KEY_DAMAGE_TAKEN_LIGHTNING 1111020806 #define QGR_KEY_SPLASH_GIVEN_LIGHTNING 1111020807 #define QGR_KEY_SPLASH_TAKEN_LIGHTNING 1111020808 #define QGR_KEY_PICKUP_LIGHTNING 1111020809 #define QGR_KEY_TIME_LIGHTNING 1111020810 #define QGR_KEY_FRAG_BFG 1211020900 #define QGR_KEY_SUICIDE_BFG 1111020901 #define QGR_KEY_SHOT_FIRED_BFG 1111020902 #define QGR_KEY_HIT_GIVEN_BFG 1111020903 #define QGR_KEY_HIT_TAKEN_BFG 1111020904 #define QGR_KEY_DAMAGE_GIVEN_BFG 1111020905 #define QGR_KEY_DAMAGE_TAKEN_BFG 1111020906 #define QGR_KEY_SPLASH_GIVEN_BFG 1111020907 #define QGR_KEY_SPLASH_TAKEN_BFG 1111020908 #define QGR_KEY_PICKUP_BFG 1111020909 #define QGR_KEY_TIME_BFG 1111020910 #define QGR_KEY_FRAG_GRAPPLE 1211021000 #define QGR_KEY_SUICIDE_GRAPPLE 1111021001 #define QGR_KEY_SHOT_FIRED_GRAPPLE 1111021002 #define QGR_KEY_HIT_GIVEN_GRAPPLE 1111021003 #define QGR_KEY_HIT_TAKEN_GRAPPLE 1111021004 #define QGR_KEY_DAMAGE_GIVEN_GRAPPLE 1111021005 #define QGR_KEY_DAMAGE_TAKEN_GRAPPLE 1111021006 #define QGR_KEY_SPLASH_GIVEN_GRAPPLE 1111021007 #define QGR_KEY_SPLASH_TAKEN_GRAPPLE 1111021008 #define QGR_KEY_PICKUP_GRAPPLE 1111021009 #define QGR_KEY_TIME_GRAPPLE 1111021010 #define QGR_KEY_FRAG_UNKNOWN 1211021100 #define QGR_KEY_SUICIDE_UNKNOWN 1111021101 #define QGR_KEY_SHOT_FIRED_UNKNOWN 1111021102 #define QGR_KEY_HIT_GIVEN_UNKNOWN 1111021103 #define QGR_KEY_HIT_TAKEN_UNKNOWN 1111021104 #define QGR_KEY_DAMAGE_GIVEN_UNKNOWN 1111021105 #define QGR_KEY_DAMAGE_TAKEN_UNKNOWN 1111021106 #define QGR_KEY_SPLASH_GIVEN_UNKNOWN 1111021107 #define QGR_KEY_SPLASH_TAKEN_UNKNOWN 1111021108 #define QGR_KEY_PICKUP_UNKNOWN 1111021109 #define QGR_KEY_TIME_UNKNOWN 1111021110 // new to team arena #define QGR_KEY_FRAG_NAILGIN 1211021200 #define QGR_KEY_SUICIDE_NAILGIN 1111021201 #define QGR_KEY_SHOT_FIRED_NAILGIN 1111021202 #define QGR_KEY_HIT_GIVEN_NAILGIN 1111021203 #define QGR_KEY_HIT_TAKEN_NAILGIN 1111021204 #define QGR_KEY_DAMAGE_GIVEN_NAILGIN 1111021205 #define QGR_KEY_DAMAGE_TAKEN_NAILGIN 1111021206 #define QGR_KEY_SPLASH_GIVEN_NAILGIN 1111021207 #define QGR_KEY_SPLASH_TAKEN_NAILGIN 1111021208 #define QGR_KEY_PICKUP_NAILGIN 1111021209 #define QGR_KEY_TIME_NAILGIN 1111021210 // new to team arena #define QGR_KEY_FRAG_PROX_LAUNCHER 1211021300 #define QGR_KEY_SUICIDE_PROX_LAUNCHER 1111021301 #define QGR_KEY_SHOT_FIRED_PROX_LAUNCHER 1111021302 #define QGR_KEY_HIT_GIVEN_PROX_LAUNCHER 1111021303 #define QGR_KEY_HIT_TAKEN_PROX_LAUNCHER 1111021304 #define QGR_KEY_DAMAGE_GIVEN_PROX_LAUNCHER 1111021305 #define QGR_KEY_DAMAGE_TAKEN_PROX_LAUNCHER 1111021306 #define QGR_KEY_SPLASH_GIVEN_PROX_LAUNCHER 1111021307 #define QGR_KEY_SPLASH_TAKEN_PROX_LAUNCHER 1111021308 #define QGR_KEY_PICKUP_PROX_LAUNCHER 1111021309 #define QGR_KEY_TIME_PROX_LAUNCHER 1111021310 // new to team arena #define QGR_KEY_FRAG_CHAINGUN 1211021400 #define QGR_KEY_SUICIDE_CHAINGUN 1111021401 #define QGR_KEY_SHOT_FIRED_CHAINGUN 1111021402 #define QGR_KEY_HIT_GIVEN_CHAINGUN 1111021403 #define QGR_KEY_HIT_TAKEN_CHAINGUN 1111021404 #define QGR_KEY_DAMAGE_GIVEN_CHAINGUN 1111021405 #define QGR_KEY_DAMAGE_TAKEN_CHAINGUN 1111021406 #define QGR_KEY_SPLASH_GIVEN_CHAINGUN 1111021407 #define QGR_KEY_SPLASH_TAKEN_CHAINGUN 1111021408 #define QGR_KEY_PICKUP_CHAINGUN 1111021409 #define QGR_KEY_TIME_CHAINGUN 1111021410 // ammo keys #define QGR_KEY_BOXES 1111030000 #define QGR_KEY_ROUNDS 1111030001 #define QGR_KEY_BOXES_BULLETS 1111030100 #define QGR_KEY_ROUNDS_BULLETS 1111030101 #define QGR_KEY_BOXES_SHELLS 1111030200 #define QGR_KEY_ROUNDS_SHELLS 1111030201 #define QGR_KEY_BOXES_GRENADES 1111030300 #define QGR_KEY_ROUNDS_GRENADES 1111030301 #define QGR_KEY_BOXES_ROCKETS 1111030400 #define QGR_KEY_ROUNDS_ROCKETS 1111030401 #define QGR_KEY_BOXES_CELLS 1111030500 #define QGR_KEY_ROUNDS_CELLS 1111030501 #define QGR_KEY_BOXES_SLUGS 1111030600 #define QGR_KEY_ROUNDS_SLUGS 1111030601 #define QGR_KEY_BOXES_LG_AMMO 1111030700 #define QGR_KEY_ROUNDS_LG_AMMO 1111030701 #define QGR_KEY_BOXES_BFG_AMMO 1111030800 #define QGR_KEY_ROUNDS_BFG_AMMO 1111030801 // new to team arena #define QGR_KEY_BOXES_NAILGUN_AMMO 1111030900 #define QGR_KEY_ROUNDS_NAILGUN_AMMO 1111030901 // new to team arena #define QGR_KEY_BOXES_PROX_LAUNCHER_AMMO 1111031000 #define QGR_KEY_ROUNDS_PROX_LAUNCHER_AMMO 1111031001 // new to team arena #define QGR_KEY_BOXES_CHAINGUN_AMMO 1111031100 #define QGR_KEY_ROUNDS_CHAINGUN_AMMO 1111031101 // health keys #define QGR_KEY_HEALTH 1111040000 #define QGR_KEY_HEALTH_TOTAL 1111040001 #define QGR_KEY_HEALTH_5 1111040100 #define QGR_KEY_HEALTH_25 1111040200 #define QGR_KEY_HEALTH_50 1111040300 #define QGR_KEY_HEALTH_MEGA 1111040400 // armor keys #define QGR_KEY_ARMOR 1111050000 #define QGR_KEY_ARMOR_TOTAL 1111050001 #define QGR_KEY_ARMOR_SHARD 1111050100 #define QGR_KEY_ARMOR_YELLOW 1111050200 #define QGR_KEY_ARMOR_RED 1111050300 // powerup keys #define QGR_KEY_POWERUP 1111060000 #define QGR_KEY_QUAD 1111060100 #define QGR_KEY_SUIT 1111060200 #define QGR_KEY_HASTE 1111060300 #define QGR_KEY_INVIS 1111060400 #define QGR_KEY_REGEN 1111060500 #define QGR_KEY_FLIGHT 1111060600 // persistant powerup keys // new to team arena #define QGR_KEY_SCOUT 1111160800 #define QGR_KEY_GUARD 1111160801 #define QGR_KEY_DOUBLER 1111160802 #define QGR_KEY_AMMOREGEN 1111160803 // holdable item keys #define QGR_KEY_MEDKIT 1111070000 #define QGR_KEY_MEDKIT_USE 1111070001 #define QGR_KEY_TELEPORTER 1111070100 #define QGR_KEY_TELEPORTER_USE 1111070101 // new to team arena #define QGR_KEY_KAMIKAZE 1111070200 #define QGR_KEY_KAMIKAZE_USE 1111070201 // new to team arena #define QGR_KEY_PORTAL 1111070300 #define QGR_KEY_PORTAL_USE 1111070301 // new to team arena #define QGR_KEY_INVULNERABILITY 1111070400 #define QGR_KEY_INVULNERABILITY_USE 1111070401 // hazard keys #define QGR_KEY_HAZARD_DEATH 1111080000 #define QGR_KEY_WATER 1111080100 #define QGR_KEY_SLIME 1111080200 #define QGR_KEY_LAVA 1111080300 #define QGR_KEY_CRUSH 1111080400 #define QGR_KEY_TELEFRAG 1111080500 #define QGR_KEY_FALLING 1111080600 #define QGR_KEY_SUICIDE_CMD 1111080700 #define QGR_KEY_TRIGGER_HURT 1111080800 #define QGR_KEY_HAZARD_MISC 1111080900 // reward keys #define QGR_KEY_IMPRESSIVE 1111090000 #define QGR_KEY_EXCELLENT 1111090100 // teammate keys #define QGR_KEY_TEAMMATE_FRAG 1211100000 #define QGR_KEY_TEAMMATE_HIT_GIVEN 1111100001 #define QGR_KEY_TEAMMATE_HIT_TAKEN 1111100002 #define QGR_KEY_TEAMMATE_DAMAGE_GIVEN 1111100003 #define QGR_KEY_TEAMMATE_DAMAGE_TAKEN 1111100004 #define QGR_KEY_TEAMMATE_SPLASH_GIVEN 1111100005 #define QGR_KEY_TEAMMATE_SPLASH_TAKEN 1111100006 #define QGR_KEY_TEAM_NAME 1100100007 // ctf keys #define QGR_KEY_FLAG_PICKUP 1111110000 #define QGR_KEY_FLAG_CAPTURE 1111110001 #endif // _G_RANKINGS_H_ openarena_0.8.8.orig/code/game/g_admin.c0000644000175000017500000027472311656310264016632 0ustar smcvsmcv/* =========================================================================== Copyright (C) 2004-2006 Tony J. White This file is part of the Open Arena source code. Originally copied from Tremulous under GPL version 2 including any later version. Several modifications, additions, and deletions were made by developers of the Open Arena source code. This shrubbot implementation is the original work of Tony J. White. Contains contributions from Wesley van Beelen, Chris Bajumpaa, Josh Menke, and Travis Maurer. The functionality of this code mimics the behaviour of the currently inactive project shrubet (http://www.etstats.com/shrubet/index.php?ver=2) by Ryan Mannion. However, shrubet was a closed-source project and none of it's code has been copied, only it's functionality. Open Arena Source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Open Arena Source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Open Arena Source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* KK-OAX TODO 1. Clean up the default admin levels to include the commands which I have added 3. Implement Disorientation in Code 4. DEBUG, DEBUG, DEBUG */ #include "g_local.h" // big ugly global buffer for use with buffered printing of long outputs static char g_bfb[ 32000 ]; // note: list ordered alphabetically g_admin_cmd_t g_admin_cmds[ ] = { {"adjustban", G_admin_adjustban, "b", "change the duration or reason of a ban. duration is specified as " "numbers followed by units 'w' (weeks), 'd' (days), 'h' (hours) or " "'m' (minutes), or seconds if no units are specified. if the duration is" " preceded by a + or -, the ban duration will be extended or shortened by" " the specified amount", "[^3ban#^7] (^5duration^7) (^5reason^7)" }, {"admintest", G_admin_admintest, "a", "display your current admin level", "" }, {"allready", G_admin_allready, "y", "makes everyone ready in intermission", "" }, {"ban", G_admin_ban, "b", "ban a player by IP and GUID with an optional expiration time and reason." " duration is specified as numbers followed by units 'w' (weeks), 'd' " "(days), 'h' (hours) or 'm' (minutes), or seconds if no units are " "specified", "[^3name|slot#|IP^7] (^5duration^7) (^5reason^7)" }, {"cancelvote", G_admin_cancelvote, "c", "cancel a vote taking place", "" }, //KK-OAX {"disorient", G_admin_disorient, "d", "disorient a player by flipping player's view and controls", "[^3name|slot#^7] (^hreason^7)" }, //{"fling", G_admin_fling, "d", // "throws the player specified", // "[^3name|slot#^7]" //}, {"help", G_admin_help, "h", "display commands available to you or help on a specific command", "(^5command^7)" }, {"kick", G_admin_kick, "k", "kick a player with an optional reason", "[^3name|slot#^7] (^5reason^7)" }, {"listadmins", G_admin_listadmins, "D", "display a list of all server admins and their levels", "(^5name|start admin#^7)" }, {"listplayers", G_admin_listplayers, "i", "display a list of players, their client numbers and their levels", "" }, {"lock", G_admin_lock, "K", "lock a team to prevent anyone from joining it", "[^3a|h^7]" }, //KK-OAX {"map", G_admin_map, "M", "load a map", "[^3mapname^7]" }, {"mute", G_admin_mute, "m", "mute a player", "[^3name|slot#^7]" }, {"namelog", G_admin_namelog, "e", "display a list of names used by recently connected players", "(^5name^7)" }, {"nextmap", G_admin_nextmap, "n", "go to the next map in the cycle", "" }, //KK-OAX {"orient", G_admin_orient, "d", "orient a player after a !disorient", "[^3name|slot#^7]" }, {"passvote", G_admin_passvote, "V", "pass a vote currently taking place", "" }, {"putteam", G_admin_putteam, "p", "move a player to a specified team", "[^3name|slot#^7] [^3h|a|s^7]" }, {"readconfig", G_admin_readconfig, "G", "reloads the admin config file and refreshes permission flags", "" }, {"rename", G_admin_rename, "N", "rename a player", "[^3name|slot#^7] [^3new name^7]" }, {"restart", G_admin_restart, "r", "restart the current map (optionally using named layout)", "" }, {"setlevel", G_admin_setlevel, "s", "sets the admin level of a player", "[^3name|slot#|admin#^7] [^3level^7]" }, {"showbans", G_admin_showbans, "B", "display a (partial) list of active bans", "(^5start at ban#^7) (^5name|IP^7)" }, //KK-OAX {"shuffle", G_admin_shuffle, "f", "Shuffles the teams" "" }, {"slap", G_admin_slap, "S", "Reduces the health of the selected player by the damage specified", "[^3name|slot#] [damage] [reason]" }, {"spec999", G_admin_spec999, "P", "move 999 pingers to the spectator team", ""}, {"time", G_admin_time, "C", "show the current local server time", ""}, {"unban", G_admin_unban, "b", "unbans a player specified by the slot as seen in showbans", "[^3ban#^7]" }, {"unlock", G_admin_unlock, "K", "unlock a locked team", "[^3a|h^7]" }, {"unmute", G_admin_mute, "m", "unmute a muted player", "[^3name|slot#^7]" }, //KK-OAX {"warn", G_admin_warn, "w", "warn a player", "[^3name|slot#^7] [reason]" } }; static int adminNumCmds = sizeof( g_admin_cmds ) / sizeof( g_admin_cmds[ 0 ] ); static int admin_level_maxname = 0; g_admin_level_t *g_admin_levels[ MAX_ADMIN_LEVELS ]; g_admin_admin_t *g_admin_admins[ MAX_ADMIN_ADMINS ]; g_admin_ban_t *g_admin_bans[ MAX_ADMIN_BANS ]; g_admin_command_t *g_admin_commands[ MAX_ADMIN_COMMANDS ]; g_admin_namelog_t *g_admin_namelog[ MAX_ADMIN_NAMELOGS ]; //KK-OAX Load us up some warnings here.... g_admin_warning_t *g_admin_warnings[ MAX_ADMIN_WARNINGS ]; qboolean G_admin_permission( gentity_t *ent, char flag ) { int i; int l = 0; char *flags; // console always wins if( !ent ) return qtrue; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) { flags = g_admin_admins[ i ]->flags; while( *flags ) { if( *flags == flag ) return qtrue; else if( *flags == '-' ) { while( *flags++ ) { if( *flags == flag ) return qfalse; if( *flags == '+' ) break; } } else if( *flags == '*' ) { while( *flags++ ) { if( *flags == flag ) return qfalse; } // flags with significance only for individuals ( // like ADMF_INCOGNITO and ADMF_IMMUTABLE are NOT covered // by the '*' wildcard. They must be specified manually. return ( flag != ADMF_INCOGNITO && flag != ADMF_IMMUTABLE ); } flags++; } l = g_admin_admins[ i ]->level; } } for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { if( g_admin_levels[ i ]->level == l ) { flags = g_admin_levels[ i ]->flags; while( *flags ) { if( *flags == flag ) return qtrue; if( *flags == '*' ) { while( *flags++ ) { if( *flags == flag ) return qfalse; } // flags with significance only for individuals ( // like ADMF_INCOGNITO and ADMF_IMMUTABLE are NOT covered // by the '*' wildcard. They must be specified manually. return ( flag != ADMF_INCOGNITO && flag != ADMF_IMMUTABLE ); } flags++; } } } return qfalse; } qboolean G_admin_name_check( gentity_t *ent, char *name, char *err, int len ) { int i; gclient_t *client; char testName[ MAX_NAME_LENGTH ] = {""}; char name2[ MAX_NAME_LENGTH ] = {""}; G_SanitiseString( name, name2, sizeof( name2 ) ); if( !Q_stricmp( name2, "UnnamedPlayer" ) ) return qtrue; for( i = 0; i < level.maxclients; i++ ) { client = &level.clients[ i ]; if( client->pers.connected == CON_DISCONNECTED ) continue; // can rename ones self to the same name using different colors if( i == ( ent - g_entities ) ) continue; G_SanitiseString( client->pers.netname, testName, sizeof( testName ) ); if( !Q_stricmp( name2, testName ) ) { Com_sprintf( err, len, "The name '%s^7' is already in use", name ); return qfalse; } } if( !g_adminNameProtect.integer ) return qtrue; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( g_admin_admins[ i ]->level < 1 ) continue; G_SanitiseString( g_admin_admins[ i ]->name, testName, sizeof( testName ) ); if( !Q_stricmp( name2, testName ) && Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) { Com_sprintf( err, len, "The name '%s^7' belongs to an admin, " "please use another name", name ); return qfalse; } } return qtrue; } static qboolean admin_higher_guid( char *admin_guid, char *victim_guid ) { int i; int alevel = 0; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( admin_guid, g_admin_admins[ i ]->guid ) ) { alevel = g_admin_admins[ i ]->level; break; } } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( victim_guid, g_admin_admins[ i ]->guid ) ) { if( alevel < g_admin_admins[ i ]->level ) return qfalse; return !strstr( g_admin_admins[ i ]->flags, va( "%c", ADMF_IMMUTABLE ) ); } } return qtrue; } static qboolean admin_higher( gentity_t *admin, gentity_t *victim ) { // console always wins if( !admin ) return qtrue; // just in case if( !victim ) return qtrue; return admin_higher_guid( admin->client->pers.guid, victim->client->pers.guid ); } //KK-OAX Moved the Read/Write int/String functions to g_fileops.c for portability //across GAME //KK-OAX Added Warnings static void admin_writeconfig( void ) { fileHandle_t f; int len, i, j; int t; char levels[ MAX_STRING_CHARS ] = {""}; if( !g_admin.string[ 0 ] ) { G_Printf( S_COLOR_YELLOW "WARNING: g_admin is not set. " " configuration will not be saved to a file.\n" ); return; } t = trap_RealTime( NULL ); len = trap_FS_FOpenFile( g_admin.string, &f, FS_WRITE ); if( len < 0 ) { G_Printf( "admin_writeconfig: could not open g_admin file \"%s\"\n", g_admin.string ); return; } for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { trap_FS_Write( "[level]\n", 8, f ); trap_FS_Write( "level = ", 10, f ); writeFile_int( g_admin_levels[ i ]->level, f ); trap_FS_Write( "name = ", 10, f ); writeFile_string( g_admin_levels[ i ]->name, f ); trap_FS_Write( "flags = ", 10, f ); writeFile_string( g_admin_levels[ i ]->flags, f ); trap_FS_Write( "\n", 1, f ); } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { // don't write level 0 users if( g_admin_admins[ i ]->level == 0 ) continue; trap_FS_Write( "[admin]\n", 8, f ); trap_FS_Write( "name = ", 10, f ); writeFile_string( g_admin_admins[ i ]->name, f ); trap_FS_Write( "guid = ", 10, f ); writeFile_string( g_admin_admins[ i ]->guid, f ); trap_FS_Write( "level = ", 10, f ); writeFile_int( g_admin_admins[ i ]->level, f ); trap_FS_Write( "flags = ", 10, f ); writeFile_string( g_admin_admins[ i ]->flags, f ); trap_FS_Write( "\n", 1, f ); } for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { // don't write expired bans // if expires is 0, then it's a perm ban if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; trap_FS_Write( "[ban]\n", 6, f ); trap_FS_Write( "name = ", 10, f ); writeFile_string( g_admin_bans[ i ]->name, f ); trap_FS_Write( "guid = ", 10, f ); writeFile_string( g_admin_bans[ i ]->guid, f ); trap_FS_Write( "ip = ", 10, f ); writeFile_string( g_admin_bans[ i ]->ip, f ); trap_FS_Write( "reason = ", 10, f ); writeFile_string( g_admin_bans[ i ]->reason, f ); trap_FS_Write( "made = ", 10, f ); writeFile_string( g_admin_bans[ i ]->made, f ); trap_FS_Write( "expires = ", 10, f ); writeFile_int( g_admin_bans[ i ]->expires, f ); trap_FS_Write( "banner = ", 10, f ); writeFile_string( g_admin_bans[ i ]->banner, f ); trap_FS_Write( "\n", 1, f ); } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { levels[ 0 ] = '\0'; trap_FS_Write( "[command]\n", 10, f ); trap_FS_Write( "command = ", 10, f ); writeFile_string( g_admin_commands[ i ]->command, f ); trap_FS_Write( "exec = ", 10, f ); writeFile_string( g_admin_commands[ i ]->exec, f ); trap_FS_Write( "desc = ", 10, f ); writeFile_string( g_admin_commands[ i ]->desc, f ); trap_FS_Write( "levels = ", 10, f ); for( j = 0; g_admin_commands[ i ]->levels[ j ] != -1; j++ ) { Q_strcat( levels, sizeof( levels ), va( "%i ", g_admin_commands[ i ]->levels[ j ] ) ); } writeFile_string( levels, f ); trap_FS_Write( "\n", 1, f ); } for( i = 0; i < MAX_ADMIN_WARNINGS && g_admin_warnings[ i ]; i++ ) { // don't write expired warnings // if expires is 0, then it's a perm warning // it will get loaded everytime they connect!!!! if( g_admin_warnings[ i ]->expires != 0 && ( g_admin_warnings[ i ]->expires - t ) < 1 ) continue; trap_FS_Write( "[warning]\n", 10, f ); trap_FS_Write( "name = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->name, f ); trap_FS_Write( "guid = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->guid, f ); trap_FS_Write( "ip = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->ip, f ); trap_FS_Write( "warning = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->warning, f ); trap_FS_Write( "made = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->made, f ); trap_FS_Write( "expires = ", 10, f ); writeFile_int( g_admin_warnings[ i ]->expires, f ); trap_FS_Write( "warner = ", 10, f ); writeFile_string( g_admin_warnings[ i ]->warner, f ); trap_FS_Write( "\n", 1, f ); } trap_FS_FCloseFile( f ); } // if we can't parse any levels from readconfig, set up default // ones to make new installs easier for admins //KK-OAX TODO: Add all features to default levels... static void admin_default_levels( void ) { g_admin_level_t *l; int i; for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { BG_Free( g_admin_levels[ i ] ); g_admin_levels[ i ] = NULL; } for( i = 0; i <= 5; i++ ) { l = BG_Alloc( sizeof( g_admin_level_t ) ); l->level = i; *l->name = '\0'; *l->flags = '\0'; g_admin_levels[ i ] = l; } Q_strncpyz( g_admin_levels[ 0 ]->name, "^4Unknown Player", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 0 ]->flags, "ahC", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 1 ]->name, "^5Server Regular", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 1 ]->flags, "iahC", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 2 ]->name, "^6Team Manager", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 2 ]->flags, "iahCpPwr", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 3 ]->name, "^2Junior Admin", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 3 ]->flags, "iahCpPwrkmfKncN?", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 4 ]->name, "^3Senior Admin", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 4 ]->flags, "iahCpPwrkmfKncN?MVdBbeDS51", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 5 ]->name, "^1Server Operator", sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 5 ]->flags, "*", sizeof( l->flags ) ); admin_level_maxname = 15; } // return a level for a player entity. int G_admin_level( gentity_t *ent ) { int i; if( !ent ) { return MAX_ADMIN_LEVELS; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) return g_admin_admins[ i ]->level; } return 0; } static qboolean admin_command_permission( gentity_t *ent, char *command ) { int i, j; int level; if( !ent ) return qtrue; level = ent->client->pers.adminLevel; for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { if( !Q_stricmp( command, g_admin_commands[ i ]->command ) ) { for( j = 0; g_admin_commands[ i ]->levels[ j ] != -1; j++ ) { if( g_admin_commands[ i ]->levels[ j ] == level ) { return qtrue; } } } } return qfalse; } static void admin_log( gentity_t *admin, char *cmd, int skiparg ) { fileHandle_t f; int len, i, j; char string[ MAX_STRING_CHARS ]; int min, tens, sec; g_admin_admin_t *a; g_admin_level_t *l; char flags[ MAX_ADMIN_FLAGS * 2 ]; gentity_t *victim = NULL; int pids[ MAX_CLIENTS ]; char name[ MAX_NAME_LENGTH ]; if( !g_adminLog.string[ 0 ] ) return; len = trap_FS_FOpenFile( g_adminLog.string, &f, FS_APPEND ); if( len < 0 ) { G_Printf( "admin_log: error could not open %s\n", g_adminLog.string ); return; } sec = level.time / 1000; min = sec / 60; sec -= min * 60; tens = sec / 10; sec -= tens * 10; *flags = '\0'; if( admin ) { for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( g_admin_admins[ i ]->guid , admin->client->pers.guid ) ) { a = g_admin_admins[ i ]; Q_strncpyz( flags, a->flags, sizeof( flags ) ); for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) { if( g_admin_levels[ j ]->level == a->level ) { l = g_admin_levels[ j ]; Q_strcat( flags, sizeof( flags ), l->flags ); break; } } break; } } } if( G_SayArgc() > 1 + skiparg ) { G_SayArgv( 1 + skiparg, name, sizeof( name ) ); if( G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) == 1 ) { victim = &g_entities[ pids[ 0 ] ]; } } if( victim && Q_stricmp( cmd, "attempted" ) ) { Com_sprintf( string, sizeof( string ), "%3i:%i%i: %i: %s: %s: %s: %s: %s: %s: \"%s\"\n", min, tens, sec, ( admin ) ? admin->s.clientNum : -1, ( admin ) ? admin->client->pers.guid : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", ( admin ) ? admin->client->pers.netname : "console", flags, cmd, victim->client->pers.guid, victim->client->pers.netname, G_SayConcatArgs( 2 + skiparg ) ); } else { Com_sprintf( string, sizeof( string ), "%3i:%i%i: %i: %s: %s: %s: %s: \"%s\"\n", min, tens, sec, ( admin ) ? admin->s.clientNum : -1, ( admin ) ? admin->client->pers.guid : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", ( admin ) ? admin->client->pers.netname : "console", flags, cmd, G_SayConcatArgs( 1 + skiparg ) ); } trap_FS_Write( string, strlen( string ), f ); trap_FS_FCloseFile( f ); } static int admin_listadmins( gentity_t *ent, int start, char *search ) { int drawn = 0; char guid_stub[9]; char name[ MAX_NAME_LENGTH ] = {""}; char name2[ MAX_NAME_LENGTH ] = {""}; char lname[ MAX_NAME_LENGTH ] = {""}; int i, j; gentity_t *vic; int l = 0; qboolean dup = qfalse; ADMBP_begin(); // print out all connected players regardless of level if name searching for( i = 0; i < level.maxclients && search[ 0 ]; i++ ) { vic = &g_entities[ i ]; if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) continue; l = vic->client->pers.adminLevel; G_SanitiseString( vic->client->pers.netname, name, sizeof( name ) ); if( !strstr( name, search ) ) continue; for( j = 0; j < 8; j++ ) guid_stub[ j ] = vic->client->pers.guid[ j + 24 ]; guid_stub[ j ] = '\0'; lname[ 0 ] = '\0'; for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) { if( g_admin_levels[ j ]->level == l ) { int k, colorlen; for( colorlen = k = 0; g_admin_levels[ j ]->name[ k ]; k++ ) if( Q_IsColorString( &g_admin_levels[ j ]->name[ k ] ) ) colorlen += 2; Com_sprintf( lname, sizeof( lname ), "%*s", admin_level_maxname + colorlen, g_admin_levels[ j ]->name ); break; } } ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", i, l, lname, guid_stub, vic->client->pers.netname ) ); drawn++; } for( i = start; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && drawn < MAX_ADMIN_LISTITEMS; i++ ) { if( search[ 0 ] ) { G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) ); if( !strstr( name, search ) ) continue; // verify we don't have the same guid/name pair in connected players // since we don't want to draw the same player twice dup = qfalse; for( j = 0; j < level.maxclients; j++ ) { vic = &g_entities[ j ]; if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) continue; G_SanitiseString( vic->client->pers.netname, name2, sizeof( name2 ) ); if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid ) && strstr( name2, search ) ) { dup = qtrue; break; } } if( dup ) continue; } for( j = 0; j < 8; j++ ) guid_stub[ j ] = g_admin_admins[ i ]->guid[ j + 24 ]; guid_stub[ j ] = '\0'; lname[ 0 ] = '\0'; for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) { if( g_admin_levels[ j ]->level == g_admin_admins[ i ]->level ) { int k, colorlen; for( colorlen = k = 0; g_admin_levels[ j ]->name[ k ]; k++ ) if( Q_IsColorString( &g_admin_levels[ j ]->name[ k ] ) ) colorlen += 2; Com_sprintf( lname, sizeof( lname ), "%*s", admin_level_maxname + colorlen, g_admin_levels[ j ]->name ); break; } } ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", ( i + MAX_CLIENTS ), g_admin_admins[ i ]->level, lname, guid_stub, g_admin_admins[ i ]->name ) ); drawn++; } ADMBP_end(); return drawn; } void G_admin_duration( int secs, char *duration, int dursize ) { if( secs > ( 60 * 60 * 24 * 365 * 50 ) || secs < 0 ) Q_strncpyz( duration, "PERMANENT", dursize ); else if( secs >= ( 60 * 60 * 24 * 365 ) ) Com_sprintf( duration, dursize, "%1.1f years", ( secs / ( 60 * 60 * 24 * 365.0f ) ) ); else if( secs >= ( 60 * 60 * 24 * 90 ) ) Com_sprintf( duration, dursize, "%1.1f weeks", ( secs / ( 60 * 60 * 24 * 7.0f ) ) ); else if( secs >= ( 60 * 60 * 24 ) ) Com_sprintf( duration, dursize, "%1.1f days", ( secs / ( 60 * 60 * 24.0f ) ) ); else if( secs >= ( 60 * 60 ) ) Com_sprintf( duration, dursize, "%1.1f hours", ( secs / ( 60 * 60.0f ) ) ); else if( secs >= 60 ) Com_sprintf( duration, dursize, "%1.1f minutes", ( secs / 60.0f ) ); else Com_sprintf( duration, dursize, "%i seconds", secs ); } qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) { char *guid, *ip; int i; int t; *reason = '\0'; t = trap_RealTime( NULL ); if( !*userinfo ) return qfalse; ip = Info_ValueForKey( userinfo, "ip" ); if( !*ip ) return qfalse; guid = Info_ValueForKey( userinfo, "cl_guid" ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { // 0 is for perm ban if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; if( strstr( ip, g_admin_bans[ i ]->ip ) ) { char duration[ 32 ]; G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) ); Com_sprintf( reason, rlen, "You have been banned by %s^7 reason: %s^7 expires: %s", g_admin_bans[ i ]->banner, g_admin_bans[ i ]->reason, duration ); G_Printf( "Banned player tried to connect from IP %s\n", ip ); return qtrue; } if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) { char duration[ 32 ]; G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) ); Com_sprintf( reason, rlen, "You have been banned by %s^7 reason: %s^7 expires: %s", g_admin_bans[ i ]->banner, g_admin_bans[ i ]->reason, duration ); G_Printf( "Banned player tried to connect with GUID %s\n", guid ); return qtrue; } } return qfalse; } qboolean G_admin_cmd_check( gentity_t *ent, qboolean say ) { int i; char command[ MAX_ADMIN_CMD_LEN ]; char *cmd; int skip = 0; command[ 0 ] = '\0'; G_SayArgv( 0, command, sizeof( command ) ); if( !command[ 0 ] ) return qfalse; if( !Q_stricmp( command, "say" ) || ( !Q_stricmp( command, "say_team" ) && G_admin_permission( ent, ADMF_TEAMCHAT_CMD ) ) ) { skip = 1; G_SayArgv( 1, command, sizeof( command ) ); } if( command[ 0 ] == '!' ) { cmd = &command[ 1 ]; } else { return qfalse; } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { if( Q_stricmp( cmd, g_admin_commands[ i ]->command ) ) continue; if( admin_command_permission( ent, cmd ) ) { // flooding say will have already been accounted for in ClientCommand if( !say && G_FloodLimited( ent ) ) return qtrue; trap_SendConsoleCommand( EXEC_APPEND, g_admin_commands[ i ]->exec ); admin_log( ent, cmd, skip ); } else { ADMP( va( "^3!%s: ^7permission denied\n", g_admin_commands[ i ]->command ) ); admin_log( ent, "attempted", skip - 1 ); } return qtrue; } for( i = 0; i < adminNumCmds; i++ ) { if( Q_stricmp( cmd, g_admin_cmds[ i ].keyword ) ) continue; if( G_admin_permission( ent, g_admin_cmds[ i ].flag[ 0 ] ) ) { // flooding say will have already been accounted for in ClientCommand if( !say && G_FloodLimited( ent ) ) return qtrue; g_admin_cmds[ i ].handler( ent, skip ); admin_log( ent, cmd, skip ); } else { ADMP( va( "^3!%s: ^7permission denied\n", g_admin_cmds[ i ].keyword ) ); admin_log( ent, "attempted", skip - 1 ); } return qtrue; } return qfalse; } void G_admin_namelog_cleanup( ) { int i; for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { BG_Free( g_admin_namelog[ i ] ); g_admin_namelog[ i ] = NULL; } } void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) { int i, j; g_admin_namelog_t *namelog; char n1[ MAX_NAME_LENGTH ]; char n2[ MAX_NAME_LENGTH ]; int clientNum = ( client - level.clients ); G_SanitiseString( client->pers.netname, n1, sizeof( n1 ) ); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { if( disconnect && g_admin_namelog[ i ]->slot != clientNum ) continue; if( !disconnect && !( g_admin_namelog[ i ]->slot == clientNum || g_admin_namelog[ i ]->slot == -1 ) ) { continue; } if( !Q_stricmp( client->pers.ip, g_admin_namelog[ i ]->ip ) && !Q_stricmp( client->pers.guid, g_admin_namelog[ i ]->guid ) ) { for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); if( !Q_stricmp( n1, n2 ) ) break; } if( j == MAX_ADMIN_NAMELOG_NAMES ) j = MAX_ADMIN_NAMELOG_NAMES - 1; Q_strncpyz( g_admin_namelog[ i ]->name[ j ], client->pers.netname, sizeof( g_admin_namelog[ i ]->name[ j ] ) ); g_admin_namelog[ i ]->slot = ( disconnect ) ? -1 : clientNum; // if this player is connecting, they are no longer banned if( !disconnect ) g_admin_namelog[ i ]->banned = qfalse; return; } } if( i >= MAX_ADMIN_NAMELOGS ) { G_Printf( "G_admin_namelog_update: warning, g_admin_namelogs overflow\n" ); return; } namelog = BG_Alloc( sizeof( g_admin_namelog_t ) ); memset( namelog, 0, sizeof( namelog ) ); for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES; j++ ) namelog->name[ j ][ 0 ] = '\0'; Q_strncpyz( namelog->ip, client->pers.ip, sizeof( namelog->ip ) ); Q_strncpyz( namelog->guid, client->pers.guid, sizeof( namelog->guid ) ); Q_strncpyz( namelog->name[ 0 ], client->pers.netname, sizeof( namelog->name[ 0 ] ) ); namelog->slot = ( disconnect ) ? -1 : clientNum; g_admin_namelog[ i ] = namelog; } //KK-OAX Added Parsing Warnings qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) { g_admin_level_t *l = NULL; g_admin_admin_t *a = NULL; g_admin_ban_t *b = NULL; g_admin_command_t *c = NULL; g_admin_warning_t *w = NULL; int lc = 0, ac = 0, bc = 0, cc = 0, wc = 0; fileHandle_t f; int len; char *cnf, *cnf2; char *t; qboolean level_open, admin_open, ban_open, command_open, warning_open; int i; G_admin_cleanup(); if( !g_admin.string[ 0 ] ) { ADMP( "^3!readconfig: g_admin is not set, not loading configuration " "from a file\n" ); admin_default_levels(); return qfalse; } len = trap_FS_FOpenFile( g_admin.string, &f, FS_READ ); if( len < 0 ) { G_Printf( "^3!readconfig: ^7could not open admin config file %s\n", g_admin.string ); admin_default_levels(); return qfalse; } cnf = BG_Alloc( len + 1 ); cnf2 = cnf; trap_FS_Read( cnf, len, f ); *( cnf + len ) = '\0'; trap_FS_FCloseFile( f ); admin_level_maxname = 0; level_open = admin_open = ban_open = command_open = warning_open = qfalse; COM_BeginParseSession( g_admin.string ); while( 1 ) { t = COM_Parse( &cnf ); if( !*t ) break; if( !Q_stricmp( t, "[level]" ) ) { if( lc >= MAX_ADMIN_LEVELS ) return qfalse; l = BG_Alloc( sizeof( g_admin_level_t ) ); g_admin_levels[ lc++ ] = l; level_open = qtrue; admin_open = ban_open = command_open = warning_open = qfalse; } else if( !Q_stricmp( t, "[admin]" ) ) { if( ac >= MAX_ADMIN_ADMINS ) return qfalse; a = BG_Alloc( sizeof( g_admin_admin_t ) ); g_admin_admins[ ac++ ] = a; admin_open = qtrue; level_open = ban_open = command_open = warning_open = qfalse; } else if( !Q_stricmp( t, "[ban]" ) ) { if( bc >= MAX_ADMIN_BANS ) return qfalse; b = BG_Alloc( sizeof( g_admin_ban_t ) ); g_admin_bans[ bc++ ] = b; ban_open = qtrue; level_open = admin_open = command_open = warning_open = qfalse; } else if( !Q_stricmp( t, "[command]" ) ) { if( cc >= MAX_ADMIN_COMMANDS ) return qfalse; c = BG_Alloc( sizeof( g_admin_command_t ) ); g_admin_commands[ cc++ ] = c; c->levels[ 0 ] = -1; command_open = qtrue; level_open = admin_open = ban_open = warning_open = qfalse; } else if( !Q_stricmp( t, "[warning]" ) ) { if( wc >= MAX_ADMIN_WARNINGS ) return qfalse; w = BG_Alloc( sizeof( g_admin_warning_t ) ); g_admin_warnings[ wc++ ] = w; warning_open = qtrue; level_open = admin_open = ban_open = command_open = qfalse; } else if( level_open ) { if( !Q_stricmp( t, "level" ) ) { readFile_int( &cnf, &l->level ); } else if( !Q_stricmp( t, "name" ) ) { readFile_string( &cnf, l->name, sizeof( l->name ) ); } else if( !Q_stricmp( t, "flags" ) ) { readFile_string( &cnf, l->flags, sizeof( l->flags ) ); } else { COM_ParseError( "[level] unrecognized token \"%s\"", t ); } } else if( admin_open ) { if( !Q_stricmp( t, "name" ) ) { readFile_string( &cnf, a->name, sizeof( a->name ) ); } else if( !Q_stricmp( t, "guid" ) ) { readFile_string( &cnf, a->guid, sizeof( a->guid ) ); } else if( !Q_stricmp( t, "level" ) ) { readFile_int( &cnf, &a->level ); } else if( !Q_stricmp( t, "flags" ) ) { readFile_string( &cnf, a->flags, sizeof( a->flags ) ); } else { COM_ParseError( "[admin] unrecognized token \"%s\"", t ); } } else if( ban_open ) { if( !Q_stricmp( t, "name" ) ) { readFile_string( &cnf, b->name, sizeof( b->name ) ); } else if( !Q_stricmp( t, "guid" ) ) { readFile_string( &cnf, b->guid, sizeof( b->guid ) ); } else if( !Q_stricmp( t, "ip" ) ) { readFile_string( &cnf, b->ip, sizeof( b->ip ) ); } else if( !Q_stricmp( t, "reason" ) ) { readFile_string( &cnf, b->reason, sizeof( b->reason ) ); } else if( !Q_stricmp( t, "made" ) ) { readFile_string( &cnf, b->made, sizeof( b->made ) ); } else if( !Q_stricmp( t, "expires" ) ) { readFile_int( &cnf, &b->expires ); } else if( !Q_stricmp( t, "banner" ) ) { readFile_string( &cnf, b->banner, sizeof( b->banner ) ); } else { COM_ParseError( "[ban] unrecognized token \"%s\"", t ); } } else if( command_open ) { if( !Q_stricmp( t, "command" ) ) { readFile_string( &cnf, c->command, sizeof( c->command ) ); } else if( !Q_stricmp( t, "exec" ) ) { readFile_string( &cnf, c->exec, sizeof( c->exec ) ); } else if( !Q_stricmp( t, "desc" ) ) { readFile_string( &cnf, c->desc, sizeof( c->desc ) ); } else if( !Q_stricmp( t, "levels" ) ) { char levels[ MAX_STRING_CHARS ] = {""}; char *level = levels; char *lp; int cmdlevel = 0; readFile_string( &cnf, levels, sizeof( levels ) ); while( cmdlevel < MAX_ADMIN_LEVELS ) { lp = COM_Parse( &level ); if( !*lp ) break; c->levels[ cmdlevel++ ] = atoi( lp ); } // ensure the list is -1 terminated c->levels[ cmdlevel ] = -1; } else { COM_ParseError( "[command] unrecognized token \"%s\"", t ); } } else if( warning_open ) { if( !Q_stricmp( t, "name" ) ) { readFile_string( &cnf, w->name, sizeof( w->name ) ); } else if( !Q_stricmp( t, "guid" ) ) { readFile_string( &cnf, w->guid, sizeof( w->guid ) ); } else if( !Q_stricmp( t, "ip" ) ) { readFile_string( &cnf, w->ip, sizeof( w->ip ) ); } else if( !Q_stricmp( t, "warning" ) ) { readFile_string( &cnf, w->warning, sizeof( w->warning ) ); } else if( !Q_stricmp( t, "made" ) ) { readFile_string( &cnf, w->made, sizeof( w->made ) ); } else if( !Q_stricmp( t, "expires" ) ) { readFile_int( &cnf, &w->expires ); } else if( !Q_stricmp( t, "warner" ) ) { readFile_string( &cnf, w->warner, sizeof( w->warner ) ); } else { COM_ParseError( "[warning] unrecognized token \"%s\"", t ); } } else { COM_ParseError( "unexpected token \"%s\"", t ); } } BG_Free( cnf2 ); ADMP( va( "^3!readconfig: ^7loaded %d levels, %d admins, %d bans, %d commands, %d warnings\n", lc, ac, bc, cc, wc ) ); if( lc == 0 ) admin_default_levels(); else { // max printable name length for formatting for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { len = Q_PrintStrlen( g_admin_levels[ i ]->name ); if( len > admin_level_maxname ) admin_level_maxname = len; } } // reset adminLevel for( i = 0; i < level.maxclients; i++ ) if( level.clients[ i ].pers.connected != CON_DISCONNECTED ) level.clients[ i ].pers.adminLevel = G_admin_level( &g_entities[ i ] ); return qtrue; } qboolean G_admin_time( gentity_t *ent, int skiparg ) { qtime_t qt; trap_RealTime( &qt ); ADMP( va( "^3!time: ^7local time is %02i:%02i:%02i\n", qt.tm_hour, qt.tm_min, qt.tm_sec ) ); return qtrue; } qboolean G_admin_setlevel( gentity_t *ent, int skiparg ) { char name[ MAX_NAME_LENGTH ] = {""}; char lstr[ 11 ]; // 10 is max strlen() for 32-bit int char adminname[ MAX_NAME_LENGTH ] = {""}; char testname[ MAX_NAME_LENGTH ] = {""}; char guid[ 33 ]; int l, i; gentity_t *vic = NULL; qboolean updated = qfalse; g_admin_admin_t *a; qboolean found = qfalse; qboolean numeric = qtrue; int matches = 0; int id = -1; if( G_SayArgc() < 3 + skiparg ) { ADMP( "^3!setlevel: ^7usage: !setlevel [name|slot#] [level]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, testname, sizeof( testname ) ); G_SayArgv( 2 + skiparg, lstr, sizeof( lstr ) ); l = atoi( lstr ); G_SanitiseString( testname, name, sizeof( name ) ); for( i = 0; i < sizeof( name ) && name[ i ]; i++ ) { if( !isdigit( name[ i ] ) ) { numeric = qfalse; break; } } if( numeric ) id = atoi( name ); if( ent && l > ent->client->pers.adminLevel ) { ADMP( "^3!setlevel: ^7you may not use !setlevel to set a level higher " "than your current level\n" ); return qfalse; } // if admin is activated for the first time on a running server, we need // to ensure at least the default levels get created if( !ent && !g_admin_levels[ 0 ] ) G_admin_readconfig(NULL, 0); for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { if( g_admin_levels[ i ]->level == l ) { found = qtrue; break; } } if( !found ) { ADMP( "^3!setlevel: ^7level is not defined\n" ); return qfalse; } if( numeric && id >= 0 && id < level.maxclients ) vic = &g_entities[ id ]; if( vic && vic->client && vic->client->pers.connected != CON_DISCONNECTED ) { Q_strncpyz( adminname, vic->client->pers.netname, sizeof( adminname ) ); Q_strncpyz( guid, vic->client->pers.guid, sizeof( guid ) ); matches = 1; } else if( numeric && id >= MAX_CLIENTS && id < MAX_CLIENTS + MAX_ADMIN_ADMINS && g_admin_admins[ id - MAX_CLIENTS ] ) { Q_strncpyz( adminname, g_admin_admins[ id - MAX_CLIENTS ]->name, sizeof( adminname ) ); Q_strncpyz( guid, g_admin_admins[ id - MAX_CLIENTS ]->guid, sizeof( guid ) ); matches = 1; } else { for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && matches < 2; i++ ) { G_SanitiseString( g_admin_admins[ i ]->name, testname, sizeof( testname ) ); if( strstr( testname, name ) ) { Q_strncpyz( adminname, g_admin_admins[ i ]->name, sizeof( adminname ) ); Q_strncpyz( guid, g_admin_admins[ i ]->guid, sizeof( guid ) ); matches++; } } for( i = 0; i < level.maxclients && matches < 2; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( matches && !Q_stricmp( level.clients[ i ].pers.guid, guid ) ) { vic = &g_entities[ i ]; continue; } G_SanitiseString( level.clients[ i ].pers.netname, testname, sizeof( testname ) ); if( strstr( testname, name ) ) { vic = &g_entities[ i ]; matches++; Q_strncpyz( guid, vic->client->pers.guid, sizeof( guid ) ); } } if( vic && vic->client) Q_strncpyz( adminname, vic->client->pers.netname, sizeof( adminname ) ); } if( matches == 0 ) { ADMP( "^3!setlevel:^7 no match. use !listplayers or !listadmins to " "find an appropriate number to use instead of name.\n" ); return qfalse; } if( matches > 1 ) { ADMP( "^3!setlevel:^7 more than one match. Use the admin number " "instead:\n" ); admin_listadmins( ent, 0, name ); return qfalse; } if( ent && !admin_higher_guid( ent->client->pers.guid, guid ) ) { ADMP( "^3!setlevel: ^7sorry, but your intended victim has a higher" " admin level than you\n" ); return qfalse; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ];i++ ) { if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) { g_admin_admins[ i ]->level = l; Q_strncpyz( g_admin_admins[ i ]->name, adminname, sizeof( g_admin_admins[ i ]->name ) ); updated = qtrue; } } if( !updated ) { if( i == MAX_ADMIN_ADMINS ) { ADMP( "^3!setlevel: ^7too many admins\n" ); return qfalse; } a = BG_Alloc( sizeof( g_admin_admin_t ) ); a->level = l; Q_strncpyz( a->name, adminname, sizeof( a->name ) ); Q_strncpyz( a->guid, guid, sizeof( a->guid ) ); *a->flags = '\0'; g_admin_admins[ i ] = a; } AP( va( "print \"^3!setlevel: ^7%s^7 was given level %d admin rights by %s\n\"", adminname, l, ( ent ) ? ent->client->pers.netname : "console" ) ); if( vic && vic->client ) vic->client->pers.adminLevel = l; if( !g_admin.string[ 0 ] ) ADMP( "^3!setlevel: ^7WARNING g_admin not set, not saving admin record " "to a file\n" ); else admin_writeconfig(); return qtrue; } static qboolean admin_create_ban( gentity_t *ent, char *netname, char *guid, char *ip, int seconds, char *reason ) { g_admin_ban_t *b = NULL; qtime_t qt; int t; int i; t = trap_RealTime( &qt ); b = BG_Alloc( sizeof( g_admin_ban_t ) ); if( !b ) return qfalse; Q_strncpyz( b->name, netname, sizeof( b->name ) ); Q_strncpyz( b->guid, guid, sizeof( b->guid ) ); Q_strncpyz( b->ip, ip, sizeof( b->ip ) ); //strftime( b->made, sizeof( b->made ), "%m/%d/%y %H:%M:%S", lt ); Com_sprintf( b->made, sizeof( b->made ), "%02i/%02i/%02i %02i:%02i:%02i", qt.tm_mon + 1, qt.tm_mday, qt.tm_year % 100, qt.tm_hour, qt.tm_min, qt.tm_sec ); if( ent ) Q_strncpyz( b->banner, ent->client->pers.netname, sizeof( b->banner ) ); else Q_strncpyz( b->banner, "console", sizeof( b->banner ) ); if( !seconds ) b->expires = 0; else b->expires = t + seconds; if( !*reason ) Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) ); else Q_strncpyz( b->reason, reason, sizeof( b->reason ) ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) ; if( i == MAX_ADMIN_BANS ) { ADMP( "^3!ban: ^7too many bans\n" ); BG_Free( b ); return qfalse; } g_admin_bans[ i ] = b; return qtrue; } //KK-OAX Copied create_ban to get Time Stuff Right (Didn't feel like writing code to parse it) static qboolean admin_create_warning( gentity_t *ent, char *netname, char *guid, char *ip, int seconds, char *warning ) { g_admin_warning_t *w = NULL; qtime_t qt; int t; int i; t = trap_RealTime( &qt ); w = BG_Alloc( sizeof( g_admin_warning_t ) ); if( !w ) return qfalse; Q_strncpyz( w->name, netname, sizeof( w->name ) ); Q_strncpyz( w->guid, guid, sizeof( w->guid ) ); Q_strncpyz( w->ip, ip, sizeof( w->ip ) ); //strftime( b->made, sizeof( b->made ), "%m/%d/%y %H:%M:%S", lt ); Com_sprintf( w->made, sizeof( w->made ), "%02i/%02i/%02i %02i:%02i:%02i", qt.tm_mon + 1, qt.tm_mday, qt.tm_year % 100, qt.tm_hour, qt.tm_min, qt.tm_sec ); if( ent ) Q_strncpyz( w->warner, ent->client->pers.netname, sizeof( w->warner ) ); else Q_strncpyz( w->warner, "console", sizeof( w->warner ) ); if( !seconds ) w->expires = 0; else w->expires = t + seconds; if( !*warning ) Q_strncpyz( w->warning, "warned by admin", sizeof( w->warning ) ); else Q_strncpyz( w->warning, warning, sizeof( w->warning ) ); for( i = 0; i < MAX_ADMIN_WARNINGS && g_admin_warnings[ i ]; i++ ) ; if( i == MAX_ADMIN_WARNINGS ) { ADMP( "^3!warn: ^7too many warnings\n" ); BG_Free( w ); return qfalse; } g_admin_warnings[ i ] = w; return qtrue; } int G_admin_parse_time( const char *time ) { int seconds = 0, num = 0; while( *time ) { if( !isdigit( *time ) ) return -1; while( isdigit( *time ) ) num = num * 10 + *time++ - '0'; if( !*time ) break; switch( *time++ ) { case 'w': num *= 7; case 'd': num *= 24; case 'h': num *= 60; case 'm': num *= 60; case 's': break; default: return -1; } seconds += num; num = 0; } if( num ) seconds += num; return seconds; } qboolean G_admin_kick( gentity_t *ent, int skiparg ) { int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ]; int minargc; gentity_t *vic; minargc = 3 + skiparg; if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) minargc = 2 + skiparg; if( G_SayArgc() < minargc ) { ADMP( "^3!kick: ^7usage: !kick [name] [reason]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); reason = G_SayConcatArgs( 2 + skiparg ); if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!kick: ^7%s\n", err ) ); return qfalse; } vic = &g_entities[ pids[ 0 ] ]; if( !admin_higher( ent, vic ) ) { ADMP( "^3!kick: ^7sorry, but your intended victim has a higher admin" " level than you\n" ); return qfalse; } if( vic->client->pers.localClient ) { ADMP( "^3!kick: ^7disconnecting the host would end the game\n" ); return qfalse; } admin_create_ban( ent, vic->client->pers.netname, vic->client->pers.guid, vic->client->pers.ip, G_admin_parse_time( va( "1s%s", g_adminTempBan.string ) ), ( *reason ) ? reason : "kicked by admin" ); if( g_admin.string[ 0 ] ) admin_writeconfig(); trap_SendServerCommand( pids[ 0 ], va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\"", ( ent ) ? va( "admin:\n%s", ent->client->pers.netname ) : "", ( *reason ) ? reason : "kicked by admin" ) ); trap_DropClient( pids[ 0 ], va( "has been kicked%s^7. reason: %s", ( ent ) ? va( " by %s", ent->client->pers.netname ) : "", ( *reason ) ? reason : "kicked by admin" ) ); return qtrue; } qboolean G_admin_ban( gentity_t *ent, int skiparg ) { int seconds; char search[ MAX_NAME_LENGTH ]; char secs[ MAX_TOKEN_CHARS ]; char *reason; int minargc; char duration[ 32 ]; int logmatch = -1, logmatches = 0; int i, j; qboolean exactmatch = qfalse; char n2[ MAX_NAME_LENGTH ]; char s2[ MAX_NAME_LENGTH ]; char guid_stub[ 9 ]; if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) { minargc = 2 + skiparg; } else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) || G_admin_permission( ent, ADMF_UNACCOUNTABLE ) || g_adminMaxBan.integer ) { minargc = 3 + skiparg; } else { minargc = 4 + skiparg; } if( G_SayArgc() < minargc ) { ADMP( "^3!ban: ^7usage: !ban [name|slot|ip] [duration] [reason]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, search, sizeof( search ) ); G_SanitiseString( search, s2, sizeof( s2 ) ); G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); seconds = G_admin_parse_time( secs ); if( seconds <= 0 ) { if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN) ) { ADMP( va( "^3!ban: ^7using your admin level's maximum ban length of %s\n", g_adminMaxBan.string ) ); seconds = G_admin_parse_time( g_adminMaxBan.string ); } else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) { seconds = 0; } else { ADMP( "^3!ban: ^7you may not issue permanent bans\n" ); return qfalse; } reason = G_SayConcatArgs( 2 + skiparg ); } else { if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && seconds > G_admin_parse_time( g_adminMaxBan.string ) ) { ADMP( va( "^3!ban: ^7ban length limited to %s for your admin level\n", g_adminMaxBan.string ) ); seconds = G_admin_parse_time( g_adminMaxBan.string ); } reason = G_SayConcatArgs( 3 + skiparg ); } for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { // skip players in the namelog who have already been banned if( g_admin_namelog[ i ]->banned ) continue; // skip disconnected players when banning on slot number if( g_admin_namelog[ i ]->slot == -1 ) continue; if( !Q_stricmp( va( "%d", g_admin_namelog[ i ]->slot ), search ) ) { logmatches = 1; logmatch = i; exactmatch = qtrue; break; } } for( i = 0; !exactmatch && i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { // skip players in the namelog who have already been banned if( g_admin_namelog[ i ]->banned ) continue; if( !Q_stricmp( g_admin_namelog[ i ]->ip, search ) ) { logmatches = 1; logmatch = i; exactmatch = qtrue; break; } for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); if( strstr( n2, s2 ) ) { if( logmatch != i ) logmatches++; logmatch = i; } } } if( !logmatches ) { ADMP( "^3!ban: ^7no player found by that name, IP, or slot number\n" ); return qfalse; } if( logmatches > 1 ) { ADMBP_begin(); ADMBP( "^3!ban: ^7multiple recent clients match name, use IP or slot#:\n" ); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { for( j = 0; j < 8; j++ ) guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ]; guid_stub[ j ] = '\0'; for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); if( strstr( n2, s2 ) ) { if( g_admin_namelog[ i ]->slot > -1 ) ADMBP( "^3" ); ADMBP( va( "%-2s (*%s) %15s ^7'%s^7'\n", ( g_admin_namelog[ i ]->slot > -1 ) ? va( "%d", g_admin_namelog[ i ]->slot ) : "-", guid_stub, g_admin_namelog[ i ]->ip, g_admin_namelog[ i ]->name[ j ] ) ); } } } ADMBP_end(); return qfalse; } if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_namelog[ logmatch ]->guid ) ) { ADMP( "^3!ban: ^7sorry, but your intended victim has a higher admin" " level than you\n" ); return qfalse; } if( !strcmp( g_admin_namelog[ logmatch ]->ip, "localhost" ) ) { ADMP( "^3!ban: ^7disconnecting the host would end the game\n" ); return qfalse; } G_admin_duration( ( seconds ) ? seconds : -1, duration, sizeof( duration ) ); admin_create_ban( ent, g_admin_namelog[ logmatch ]->name[ 0 ], g_admin_namelog[ logmatch ]->guid, g_admin_namelog[ logmatch ]->ip, seconds, reason ); g_admin_namelog[ logmatch ]->banned = qtrue; if( !g_admin.string[ 0 ] ) ADMP( "^3!ban: ^7WARNING g_admin not set, not saving ban to a file\n" ); else if(strlen(g_admin_namelog[ logmatch ]->guid)==0 || strlen(g_admin_namelog[ logmatch ]->ip) ) ADMP( "^3!ban: ^7WARNING bot or without GUID or IP cannot write to ban file\n"); else admin_writeconfig(); if( g_admin_namelog[ logmatch ]->slot == -1 ) { // client is already disconnected so stop here AP( va( "print \"^3!ban:^7 %s^7 has been banned by %s^7, " "duration: %s, reason: %s\n\"", g_admin_namelog[ logmatch ]->name[ 0 ], ( ent ) ? ent->client->pers.netname : "console", duration, ( *reason ) ? reason : "banned by admin" ) ); return qtrue; } trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot, va( "disconnect \"You have been banned.\n" "admin:\n%s^7\nduration:\n%s\nreason:\n%s\"", ( ent ) ? ent->client->pers.netname : "console", duration, ( *reason ) ? reason : "banned by admin" ) ); trap_DropClient( g_admin_namelog[ logmatch ]->slot, va( "has been banned by %s^7, duration: %s, reason: %s", ( ent ) ? ent->client->pers.netname : "console", duration, ( *reason ) ? reason : "banned by admin" ) ); return qtrue; } qboolean G_admin_unban( gentity_t *ent, int skiparg ) { int bnum; int time = trap_RealTime( NULL ); char bs[ 5 ]; if( G_SayArgc() < 2 + skiparg ) { ADMP( "^3!unban: ^7usage: !unban [ban#]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); bnum = atoi( bs ); if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1 ] ) { ADMP( "^3!unban: ^7invalid ban#\n" ); return qfalse; } if( g_admin_bans[ bnum - 1 ]->expires == 0 && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) { ADMP( "^3!unban: ^7you cannot remove permanent bans\n" ); return qfalse; } if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && g_admin_bans[ bnum - 1 ]->expires - time > G_admin_parse_time( g_adminMaxBan.string ) ) { ADMP( va( "^3!unban: ^7your admin level cannot remove bans longer than %s\n", g_adminMaxBan.string ) ); return qfalse; } g_admin_bans[ bnum - 1 ]->expires = time; AP( va( "print \"^3!unban: ^7ban #%d for %s^7 has been removed by %s\n\"", bnum, g_admin_bans[ bnum - 1 ]->name, ( ent ) ? ent->client->pers.netname : "console" ) ); if( g_admin.string[ 0 ] ) admin_writeconfig(); return qtrue; } qboolean G_admin_adjustban( gentity_t *ent, int skiparg ) { int bnum; int length; int expires; int time = trap_RealTime( NULL ); char duration[ 32 ] = {""}; char *reason; char bs[ 5 ]; char secs[ MAX_TOKEN_CHARS ]; char mode = '\0'; g_admin_ban_t *ban; if( G_SayArgc() < 3 + skiparg ) { ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [duration] [reason]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); bnum = atoi( bs ); if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1 ] ) { ADMP( "^3!adjustban: ^7invalid ban#\n" ); return qfalse; } ban = g_admin_bans[ bnum - 1 ]; if( ban->expires == 0 && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) { ADMP( "^3!adjustban: ^7you cannot modify permanent bans\n" ); return qfalse; } if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && ban->expires - time > G_admin_parse_time( g_adminMaxBan.string ) ) { ADMP( va( "^3!adjustban: ^7your admin level cannot modify bans longer than %s\n", g_adminMaxBan.string ) ); return qfalse; } G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); if( secs[ 0 ] == '+' || secs[ 0 ] == '-' ) mode = secs[ 0 ]; length = G_admin_parse_time( &secs[ mode ? 1 : 0 ] ); if( length < 0 ) skiparg--; else { if( length ) { if( ban->expires == 0 && mode ) { ADMP( "^3!adjustban: ^7new duration must be explicit\n" ); return qfalse; } if( mode == '+' ) expires = ban->expires + length; else if( mode == '-' ) expires = ban->expires - length; else expires = time + length; if( expires <= time ) { ADMP( "^3!adjustban: ^7ban duration must be positive\n" ); return qfalse; } if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && expires - time > G_admin_parse_time( g_adminMaxBan.string ) ) { ADMP( va( "^3!adjustban: ^7ban length is limited to %s for your admin level\n", g_adminMaxBan.string ) ); length = G_admin_parse_time( g_adminMaxBan.string ); expires = time + length; } } else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) expires = 0; else { ADMP( "^3!adjustban: ^7ban duration must be positive\n" ); return qfalse; } ban->expires = expires; G_admin_duration( ( expires ) ? expires - time : -1, duration, sizeof( duration ) ); } reason = G_SayConcatArgs( 3 + skiparg ); if( *reason ) Q_strncpyz( ban->reason, reason, sizeof( ban->reason ) ); AP( va( "print \"^3!adjustban: ^7ban #%d for %s^7 has been updated by %s^7 " "%s%s%s%s%s\n\"", bnum, ban->name, ( ent ) ? ent->client->pers.netname : "console", ( length >= 0 ) ? "duration: " : "", duration, ( length >= 0 && *reason ) ? ", " : "", ( *reason ) ? "reason: " : "", reason ) ); if( ent ) Q_strncpyz( ban->banner, ent->client->pers.netname, sizeof( ban->banner ) ); if( g_admin.string[ 0 ] ) admin_writeconfig(); return qtrue; } qboolean G_admin_putteam( gentity_t *ent, int skiparg ) { int pids[ MAX_CLIENTS ], found; //KK-OAPub Changed Team Name Length so "Spectator" doesn't crash Game char name[ MAX_NAME_LENGTH ], team[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; gentity_t *vic; team_t teamnum = TEAM_NONE; G_SayArgv( 1 + skiparg, name, sizeof( name ) ); G_SayArgv( 2 + skiparg, team, sizeof( team ) ); if( G_SayArgc() < 3 + skiparg ) { ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s]\n" ); return qfalse; } if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!putteam: ^7%s\n", err ) ); return qfalse; } if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) { ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " " admin level than you\n" ); return qfalse; } vic = &g_entities[ pids[ 0 ] ]; teamnum = G_TeamFromString( team ); if( teamnum == TEAM_NUM_TEAMS ) { ADMP( va( "^3!putteam: ^7unknown team %s\n", team ) ); return qfalse; } if( vic->client->sess.sessionTeam == teamnum ) return qfalse; SetTeam( vic, team ); AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team\n\"", ( ent ) ? ent->client->pers.netname : "console", vic->client->pers.netname, BG_TeamName( teamnum ) ) ); return qtrue; } //KK-Fixed!!!! //KK-Removed Layouts from The command qboolean G_admin_map( gentity_t *ent, int skiparg ) { char map[ MAX_QPATH ]; if( G_SayArgc( ) < 2 + skiparg ) { ADMP( "^3!map: ^7usage: !map [map] (layout)\n" ); return qfalse; } G_SayArgv( skiparg + 1, map, sizeof( map ) ); if( !trap_FS_FOpenFile( va( "maps/%s.bsp", map ), NULL, FS_READ ) ) { ADMP( va( "^3!map: ^7invalid map name '%s'\n", map ) ); return qfalse; } trap_SendConsoleCommand( EXEC_APPEND, va( "map %s", map ) ); level.restarted = qtrue; AP( va( "print \"^3!map: ^7map '%s' started by %s\n\"", map, ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_mute( gentity_t *ent, int skiparg ) { int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; char command[ MAX_ADMIN_CMD_LEN ], *cmd; gentity_t *vic; G_SayArgv( skiparg, command, sizeof( command ) ); cmd = command; if( cmd && *cmd == '!' ) cmd++; if( G_SayArgc() < 2 + skiparg ) { ADMP( va( "^3!%s: ^7usage: !%s [name|slot#]\n", cmd, cmd ) ); return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); return qfalse; } if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) { ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" " level than you\n", cmd ) ); return qfalse; } vic = &g_entities[ pids[ 0 ] ]; if( vic->client->pers.muted == qtrue ) { if( !Q_stricmp( cmd, "mute" ) ) { ADMP( "^3!mute: ^7player is already muted\n" ); return qtrue; } vic->client->pers.muted = qfalse; CPx( pids[ 0 ], "cp \"^1You have been unmuted\"" ); AP( va( "print \"^3!unmute: ^7%s^7 has been unmuted by %s\n\"", vic->client->pers.netname, ( ent ) ? ent->client->pers.netname : "console" ) ); } else { if( !Q_stricmp( cmd, "unmute" ) ) { ADMP( "^3!unmute: ^7player is not currently muted\n" ); return qtrue; } vic->client->pers.muted = qtrue; CPx( pids[ 0 ], "cp \"^1You've been muted\"" ); AP( va( "print \"^3!mute: ^7%s^7 has been muted by ^7%s\n\"", vic->client->pers.netname, ( ent ) ? ent->client->pers.netname : "console" ) ); } return qtrue; } qboolean G_admin_listadmins( gentity_t *ent, int skiparg ) { int i, found = 0; char search[ MAX_NAME_LENGTH ] = {""}; char s[ MAX_NAME_LENGTH ] = {""}; int start = 0; qboolean numeric = qtrue; int drawn = 0; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( g_admin_admins[ i ]->level == 0 ) continue; found++; } if( !found ) { ADMP( "^3!listadmins: ^7no admins defined\n" ); return qfalse; } if( G_SayArgc() == 2 + skiparg ) { G_SayArgv( 1 + skiparg, s, sizeof( s ) ); for( i = 0; i < sizeof( s ) && s[ i ]; i++ ) { if( isdigit( s[ i ] ) ) continue; numeric = qfalse; } if( numeric ) { start = atoi( s ); if( start > 0 ) start -= 1; else if( start < 0 ) start = found + start; } else G_SanitiseString( s, search, sizeof( search ) ); } if( start >= found || start < 0 ) start = 0; if( start >= found ) { ADMP( va( "^3!listadmins: ^7listing %d admins\n", found ) ); return qfalse; } drawn = admin_listadmins( ent, start, search ); if( search[ 0 ] ) { ADMP( va( "^3!listadmins:^7 found %d admins matching '%s^7'\n", drawn, search ) ); } else { ADMBP_begin(); ADMBP( va( "^3!listadmins:^7 showing admin %d - %d of %d. ", ( found ) ? ( start + 1 ) : 0, ( ( start + MAX_ADMIN_LISTITEMS ) > found ) ? found : ( start + MAX_ADMIN_LISTITEMS ), found ) ); if( ( start + MAX_ADMIN_LISTITEMS ) < found ) { ADMBP( va( "run '!listadmins %d' to see more", ( start + MAX_ADMIN_LISTITEMS + 1 ) ) ); } ADMBP( "\n" ); ADMBP_end(); } return qtrue; } qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) { int i, j; gclient_t *p; char c[ 3 ], t[ 2 ]; // color and team letter char n[ MAX_NAME_LENGTH ] = {""}; char n2[ MAX_NAME_LENGTH ] = {""}; char n3[ MAX_NAME_LENGTH ] = {""}; char lname[ MAX_NAME_LENGTH ]; char guid_stub[ 9 ]; char muted[ 2 ]; int l; ADMBP_begin(); ADMBP( va( "^3!listplayers: ^7%d players connected:\n", level.numConnectedClients ) ); for( i = 0; i < level.maxclients; i++ ) { p = &level.clients[ i ]; Q_strncpyz( t, "S", sizeof( t ) ); Q_strncpyz( c, S_COLOR_YELLOW, sizeof( c ) ); if( p->sess.sessionTeam == TEAM_BLUE ) { Q_strncpyz( t, "B", sizeof( t ) ); Q_strncpyz( c, S_COLOR_BLUE, sizeof( c ) ); } else if( p->sess.sessionTeam == TEAM_RED ) { Q_strncpyz( t, "R", sizeof( t ) ); Q_strncpyz( c, S_COLOR_RED, sizeof( c ) ); } else if( p->sess.sessionTeam == TEAM_FREE ) { Q_strncpyz( t, "F", sizeof( t ) ); Q_strncpyz( c, S_COLOR_GREEN, sizeof( c ) ); } else if( p->sess.sessionTeam == TEAM_NONE ) { Q_strncpyz( t, "S", sizeof( t ) ); Q_strncpyz( c, S_COLOR_WHITE, sizeof( c ) ); } if( p->pers.connected == CON_CONNECTING ) { Q_strncpyz( t, "C", sizeof( t ) ); Q_strncpyz( c, S_COLOR_CYAN, sizeof( c ) ); } else if( p->pers.connected != CON_CONNECTED ) { continue; } for( j = 0; j < 8; j++ ) guid_stub[ j ] = p->pers.guid[ j + 24 ]; guid_stub[ j ] = '\0'; muted[ 0 ] = '\0'; if( p->pers.muted ) { Q_strncpyz( muted, "M", sizeof( muted ) ); } //Put DisOriented Junk Here!!! l = 0; G_SanitiseString( p->pers.netname, n2, sizeof( n2 ) ); n[ 0 ] = '\0'; for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) { if( !Q_stricmp( g_admin_admins[ j ]->guid, p->pers.guid ) ) { // don't gather aka or level info if the admin is incognito if( ent && G_admin_permission( &g_entities[ i ], ADMF_INCOGNITO ) ) { break; } l = g_admin_admins[ j ]->level; G_SanitiseString( g_admin_admins[ j ]->name, n3, sizeof( n3 ) ); if( Q_stricmp( n2, n3 ) ) { Q_strncpyz( n, g_admin_admins[ j ]->name, sizeof( n ) ); } break; } } lname[ 0 ] = '\0'; for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) { if( g_admin_levels[ j ]->level == l ) { int k, colorlen; for( colorlen = k = 0; g_admin_levels[ j ]->name[ k ]; k++ ) if( Q_IsColorString( &g_admin_levels[ j ]->name[ k ] ) ) colorlen += 2; Com_sprintf( lname, sizeof( lname ), "%*s", admin_level_maxname + colorlen, g_admin_levels[ j ]->name ); break; } } ADMBP( va( "%2i %s%s^7 %-2i %s^7 (*%s) ^1%1s^7 %s^7 %s%s^7%s\n", i, c, t, l, lname, guid_stub, muted, p->pers.netname, ( *n ) ? "(a.k.a. " : "", n, ( *n ) ? ")" : "" ) ); } ADMBP_end(); return qtrue; } qboolean G_admin_showbans( gentity_t *ent, int skiparg ) { int i, found = 0; int max = -1, count; int t; char duration[ 32 ]; int max_name = 1, max_banner = 1, colorlen; int len; int secs; int start = 0; char filter[ MAX_NAME_LENGTH ] = {""}; char date[ 11 ]; char *made; int j, k; char n1[ MAX_NAME_LENGTH * 2 ] = {""}; char n2[ MAX_NAME_LENGTH * 2 ] = {""}; qboolean numeric = qtrue; char *ip_match = NULL; int ip_match_len = 0; char name_match[ MAX_NAME_LENGTH ] = {""}; t = trap_RealTime( NULL ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) { continue; } found++; max = i; } if( max < 0 ) { ADMP( "^3!showbans: ^7no bans to display\n" ); return qfalse; } if( G_SayArgc() >= 2 + skiparg ) { G_SayArgv( 1 + skiparg, filter, sizeof( filter ) ); if( G_SayArgc() >= 3 + skiparg ) { start = atoi( filter ); G_SayArgv( 2 + skiparg, filter, sizeof( filter ) ); } for( i = 0; i < sizeof( filter ) && filter[ i ] ; i++ ) { if( !isdigit( filter[ i ] ) && filter[ i ] != '.' && filter[ i ] != '-' ) { numeric = qfalse; break; } } if( !numeric ) { G_SanitiseString( filter, name_match, sizeof( name_match ) ); } else if( strchr( filter, '.' ) ) { ip_match = filter; ip_match_len = strlen(ip_match); } else { start = atoi( filter ); filter[ 0 ] = '\0'; } // showbans 1 means start with ban 0 if( start > 0 ) start--; else if( start < 0 ) { for( i = max, count = 0; i >= 0 && count < -start; i-- ) if( g_admin_bans[ i ]->expires == 0 || ( g_admin_bans[ i ]->expires - t ) > 0 ) count++; start = i + 1; } } if( start < 0 ) start = 0; if( start > max ) { ADMP( va( "^3!showbans: ^7%d is the last valid ban\n", max + 1 ) ); return qfalse; } for( i = start, count = 0; i <= max && count < MAX_ADMIN_SHOWBANS; i++ ) { if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; if( name_match[ 0 ] ) { G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); if( !strstr( n1, name_match) ) continue; } if( ip_match && Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len ) ) continue; count++; len = Q_PrintStrlen( g_admin_bans[ i ]->name ); if( len > max_name ) max_name = len; len = Q_PrintStrlen( g_admin_bans[ i ]->banner ); if( len > max_banner ) max_banner = len; } ADMBP_begin(); for( i = start, count = 0; i <= max && count < MAX_ADMIN_SHOWBANS; i++ ) { if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; if( name_match[ 0 ] ) { G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); if( !strstr( n1, name_match) ) continue; } if( ip_match && Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len ) ) continue; count++; // only print out the the date part of made date[ 0 ] = '\0'; made = g_admin_bans[ i ]->made; for( j = 0; made && *made; j++ ) { if( ( j + 1 ) >= sizeof( date ) ) break; if( *made == ' ' ) break; date[ j ] = *made; date[ j + 1 ] = '\0'; made++; } secs = ( g_admin_bans[ i ]->expires - t ); G_admin_duration( secs, duration, sizeof( duration ) ); for( colorlen = k = 0; g_admin_bans[ i ]->name[ k ]; k++ ) if( Q_IsColorString( &g_admin_bans[ i ]->name[ k ] ) ) colorlen += 2; Com_sprintf( n1, sizeof( n1 ), "%*s", max_name + colorlen, g_admin_bans[ i ]->name ); for( colorlen = k = 0; g_admin_bans[ i ]->banner[ k ]; k++ ) if( Q_IsColorString( &g_admin_bans[ i ]->banner[ k ] ) ) colorlen += 2; Com_sprintf( n2, sizeof( n2 ), "%*s", max_banner + colorlen, g_admin_bans[ i ]->banner ); ADMBP( va( "%4i %s^7 %-15s %-8s %s^7 %-10s\n \\__ %s\n", ( i + 1 ), n1, g_admin_bans[ i ]->ip, date, n2, duration, g_admin_bans[ i ]->reason ) ); } if( name_match[ 0 ] || ip_match ) { ADMBP( va( "^3!showbans:^7 found %d matching bans by %s. ", count, ( ip_match ) ? "IP" : "name" ) ); } else { ADMBP( va( "^3!showbans:^7 showing bans %d - %d of %d (%d total).", ( found ) ? ( start + 1 ) : 0, i, max + 1, found ) ); } if( i <= max ) ADMBP( va( " run !showbans %d%s%s to see more", i + 1, ( filter[ 0 ] ) ? " " : "", ( filter[ 0 ] ) ? filter : "" ) ); ADMBP( "\n" ); ADMBP_end(); return qtrue; } qboolean G_admin_help( gentity_t *ent, int skiparg ) { int i; if( G_SayArgc() < 2 + skiparg ) { int j = 0; int count = 0; ADMBP_begin(); for( i = 0; i < adminNumCmds; i++ ) { if( G_admin_permission( ent, g_admin_cmds[ i ].flag[ 0 ] ) ) { ADMBP( va( "^3!%-12s", g_admin_cmds[ i ].keyword ) ); j++; count++; } // show 6 commands per line if( j == 6 ) { ADMBP( "\n" ); j = 0; } } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { if( ! admin_command_permission( ent, g_admin_commands[ i ]->command ) ) continue; ADMBP( va( "^3!%-12s", g_admin_commands[ i ]->command ) ); j++; count++; // show 6 commands per line if( j == 6 ) { ADMBP( "\n" ); j = 0; } } if( count ) ADMBP( "\n" ); ADMBP( va( "^3!help: ^7%i available commands\n", count ) ); ADMBP( "run !help [^3command^7] for help with a specific command.\n" ); ADMBP_end(); return qtrue; } else { //!help param char param[ MAX_ADMIN_CMD_LEN ]; char *cmd; G_SayArgv( 1 + skiparg, param, sizeof( param ) ); cmd = ( param[0] == '!' ) ? ¶m[1] : ¶m[0]; ADMBP_begin(); for( i = 0; i < adminNumCmds; i++ ) { if( !Q_stricmp( cmd, g_admin_cmds[ i ].keyword ) ) { if( !G_admin_permission( ent, g_admin_cmds[ i ].flag[ 0 ] ) ) { ADMBP( va( "^3!help: ^7you do not have permission to use '%s'\n", g_admin_cmds[ i ].keyword ) ); ADMBP_end(); return qfalse; } ADMBP( va( "^3!help: ^7help for '!%s':\n", g_admin_cmds[ i ].keyword ) ); ADMBP( va( " ^3Function: ^7%s\n", g_admin_cmds[ i ].function ) ); ADMBP( va( " ^3Syntax: ^7!%s %s\n", g_admin_cmds[ i ].keyword, g_admin_cmds[ i ].syntax ) ); ADMBP( va( " ^3Flag: ^7'%c'\n", g_admin_cmds[ i ].flag[ 0 ] ) ); ADMBP_end(); return qtrue; } } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { if( !Q_stricmp( cmd, g_admin_commands[ i ]->command ) ) { if( !admin_command_permission( ent, g_admin_commands[ i ]->command ) ) { ADMBP( va( "^3!help: ^7you do not have permission to use '%s'\n", g_admin_commands[ i ]->command ) ); ADMBP_end(); return qfalse; } ADMBP( va( "^3!help: ^7help for '%s':\n", g_admin_commands[ i ]->command ) ); ADMBP( va( " ^3Description: ^7%s\n", g_admin_commands[ i ]->desc ) ); ADMBP( va( " ^3Syntax: ^7!%s\n", g_admin_commands[ i ]->command ) ); ADMBP_end(); return qtrue; } } ADMBP( va( "^3!help: ^7no help found for '%s'\n", cmd ) ); ADMBP_end(); return qfalse; } } qboolean G_admin_admintest( gentity_t *ent, int skiparg ) { int i, l = 0; qboolean found = qfalse; qboolean lname = qfalse; if( !ent ) { ADMP( "^3!admintest: ^7you are on the console.\n" ); return qtrue; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) { found = qtrue; break; } } if( found ) { l = g_admin_admins[ i ]->level; for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { if( g_admin_levels[ i ]->level != l ) continue; if( *g_admin_levels[ i ]->name ) { lname = qtrue; break; } } } AP( va( "print \"^3!admintest: ^7%s^7 is a level %d admin %s%s^7%s\n\"", ent->client->pers.netname, l, ( lname ) ? "(" : "", ( lname ) ? g_admin_levels[ i ]->name : "", ( lname ) ? ")" : "" ) ); return qtrue; } qboolean G_admin_allready( gentity_t *ent, int skiparg ) { int i = 0; gclient_t *cl; if( !level.intermissiontime ) { ADMP( "^3!allready: ^7this command is only valid during intermission\n" ); return qfalse; } for( i = 0; i < g_maxclients.integer; i++ ) { cl = level.clients + i; if( cl->pers.connected != CON_CONNECTED ) continue; if( cl->sess.sessionTeam == TEAM_NONE ) continue; cl->readyToExit = 1; } AP( va( "print \"^3!allready:^7 %s^7 says everyone is READY now\n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_cancelvote( gentity_t *ent, int skiparg ) { if(!level.voteTime && !level.teamVoteTime[ 0 ] && !level.teamVoteTime[ 1 ] ) { ADMP( "^3!cancelvote: ^7no vote in progress\n" ); return qfalse; } level.voteNo = level.numConnectedClients; level.voteYes = 0; CheckVote( ); level.teamVoteNo[ 0 ] = level.numConnectedClients; level.teamVoteYes[ 0 ] = 0; CheckTeamVote( TEAM_RED ); level.teamVoteNo[ 1 ] = level.numConnectedClients; level.teamVoteYes[ 1 ] = 0; CheckTeamVote( TEAM_BLUE ); AP( va( "print \"^3!cancelvote: ^7%s^7 decided that everyone voted No\n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_passvote( gentity_t *ent, int skiparg ) { if(!level.voteTime && !level.teamVoteTime[ 0 ] && !level.teamVoteTime[ 1 ] ) { ADMP( "^3!passvote: ^7no vote in progress\n" ); return qfalse; } level.voteYes = level.numConnectedClients; level.voteNo = 0; CheckVote( ); level.teamVoteYes[ 0 ] = level.numConnectedClients; level.teamVoteNo[ 0 ] = 0; CheckTeamVote( TEAM_RED ); level.teamVoteYes[ 1 ] = level.numConnectedClients; level.teamVoteNo[ 1 ] = 0; CheckTeamVote( TEAM_BLUE ); AP( va( "print \"^3!passvote: ^7%s^7 decided that everyone voted Yes\n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_spec999( gentity_t *ent, int skiparg ) { int i; gentity_t *vic; for( i = 0; i < level.maxclients; i++ ) { vic = &g_entities[ i ]; if( !vic->client ) continue; if( vic->client->pers.connected != CON_CONNECTED ) continue; if( vic->client->sess.sessionTeam == TEAM_NONE ) continue; if( vic->client->ps.ping == 999 ) { SetTeam( vic, "spectator" ); AP( va( "print \"^3!spec999: ^7%s^7 moved ^7%s^7 to spectators\n\"", ( ent ) ? ent->client->pers.netname : "console", vic->client->pers.netname ) ); } } return qtrue; } qboolean G_admin_rename( gentity_t *ent, int skiparg ) { int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char oldname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; char userinfo[ MAX_INFO_STRING ]; char *s; gentity_t *victim = NULL; if( G_SayArgc() < 3 + skiparg ) { ADMP( "^3!rename: ^7usage: !rename [name] [newname]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); s = G_SayConcatArgs( 2 + skiparg ); Q_strncpyz( newname, s, sizeof( newname ) ); if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!rename: ^7%s\n", err ) ); return qfalse; } victim = &g_entities[ pids[ 0 ] ]; if( !admin_higher( ent, victim ) ) { ADMP( "^3!rename: ^7sorry, but your intended victim has a higher admin" " level than you\n" ); return qfalse; } if( !G_admin_name_check( victim, newname, err, sizeof( err ) ) ) { ADMP( va( "^3!rename: ^7%s\n", err ) ); return qfalse; } //KK-OAX Since NameChanges are not going to be implemented just yet...let's ignore this. level.clients[ pids[ 0 ] ].pers.nameChanges--; level.clients[ pids[ 0 ] ].pers.nameChangeTime = 0; trap_GetUserinfo( pids[ 0 ], userinfo, sizeof( userinfo ) ); s = Info_ValueForKey( userinfo, "name" ); Q_strncpyz( oldname, s, sizeof( oldname ) ); Info_SetValueForKey( userinfo, "name", newname ); trap_SetUserinfo( pids[ 0 ], userinfo ); ClientUserinfoChanged( pids[ 0 ] ); AP( va( "print \"^3!rename: ^7%s^7 has been renamed to %s^7 by %s\n\"", oldname, newname, ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } //KK-Will Fix this For OAPub qboolean G_admin_restart( gentity_t *ent, int skiparg ) { char layout[ MAX_CVAR_VALUE_STRING ] = { "" }; if( G_SayArgc( ) > 1 + skiparg ) { char map[ MAX_QPATH ]; trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); G_SayArgv( skiparg + 1, layout, sizeof( layout ) ); } trap_SendConsoleCommand( EXEC_APPEND, "map_restart" ); AP( va( "print \"^3!restart: ^7map restarted by %s \n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_nextmap( gentity_t *ent, int skiparg ) { AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); //level.lastWin = TEAM_NONE; //trap_SetConfigstring( CS_WINNER, "NextMap" ); LogExit( va( "nextmap was run by %s", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_namelog( gentity_t *ent, int skiparg ) { int i, j; char search[ MAX_NAME_LENGTH ] = {""}; char s2[ MAX_NAME_LENGTH ] = {""}; char n2[ MAX_NAME_LENGTH ] = {""}; char guid_stub[ 9 ]; qboolean found = qfalse; int printed = 0; if( G_SayArgc() > 1 + skiparg ) { G_SayArgv( 1 + skiparg, search, sizeof( search ) ); G_SanitiseString( search, s2, sizeof( s2 ) ); } ADMBP_begin(); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { if( search[ 0 ] ) { found = qfalse; for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); if( strstr( n2, s2 ) ) { found = qtrue; break; } } if( !found ) continue; } printed++; for( j = 0; j < 8; j++ ) guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ]; guid_stub[ j ] = '\0'; if( g_admin_namelog[ i ]->slot > -1 ) ADMBP( "^3" ); ADMBP( va( "%-2s (*%s) %15s^7", ( g_admin_namelog[ i ]->slot > -1 ) ? va( "%d", g_admin_namelog[ i ]->slot ) : "-", guid_stub, g_admin_namelog[ i ]->ip ) ); for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { ADMBP( va( " '%s^7'", g_admin_namelog[ i ]->name[ j ] ) ); } ADMBP( "\n" ); } ADMBP( va( "^3!namelog:^7 %d recent clients found\n", printed ) ); ADMBP_end(); return qtrue; } qboolean G_admin_lock( gentity_t *ent, int skiparg ) { char teamName[2] = {""}; team_t team; if( G_SayArgc() < 2 + skiparg ) { ADMP( "^3!lock: ^7usage: !lock [r|b|f]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) ); team = G_TeamFromString( teamName ); if( team == TEAM_RED ) { if( level.RedTeamLocked ) { ADMP( "^3!lock: ^7the Red team is already locked\n" ); return qfalse; } level.RedTeamLocked = qtrue; } else if( team == TEAM_BLUE ) { if( level.BlueTeamLocked ) { ADMP( "^3!lock: ^7the Blue team is already locked\n" ); return qfalse; } level.BlueTeamLocked = qtrue; } else if(team == TEAM_FREE ) { if( level.FFALocked ) { ADMP( "^3!lock: ^7DeathMatch is already Locked!!!\n" ); return qfalse; } level.FFALocked = qtrue; } else { ADMP( va( "^3!lock: ^7invalid team\"%c\"\n", teamName[0] ) ); return qfalse; } AP( va( "print \"^3!lock: ^7the %s team has been locked by %s\n\"", BG_TeamName( team ), ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } qboolean G_admin_unlock( gentity_t *ent, int skiparg ) { char teamName[2] = {""}; team_t team; if( G_SayArgc() < 2 + skiparg ) { ADMP( "^3!unlock: ^7usage: !unlock [r|b|f]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) ); team = G_TeamFromString( teamName ); if( team == TEAM_RED ) { if( !level.RedTeamLocked ) { ADMP( "^3!unlock: ^7the Red team is not currently locked\n" ); return qfalse; } level.RedTeamLocked = qfalse; } else if( team == TEAM_BLUE ) { if( !level.BlueTeamLocked ) { ADMP( "^3!unlock: ^7the Blue team is not currently locked\n" ); return qfalse; } level.BlueTeamLocked = qfalse; } else if( team == TEAM_FREE ) { if( !level.FFALocked ) { ADMP( "^!unlock: ^7Deathmatch is not currently Locked!!!\n" ); return qfalse; } level.FFALocked = qfalse; } else { ADMP( va( "^3!unlock: ^7invalid team\"%c\"\n", teamName[0] ) ); return qfalse; } AP( va( "print \"^3!unlock: ^7the %s team has been unlocked by %s\n\"", BG_TeamName( team ), ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } //KK-OAX Begin Addition qboolean G_admin_disorient(gentity_t *ent, int skiparg) { int pids[MAX_CLIENTS], found; char name[MAX_NAME_LENGTH], err[MAX_STRING_CHARS]; char *reason; gentity_t *vic; if(G_SayArgc() < 2+skiparg) { ADMP("^/disorient usage: ^7!disorient [name|slot#] [reason]"); return qfalse; } G_SayArgv(1+skiparg, name, sizeof(name)); reason = G_SayConcatArgs(2+skiparg); if((found = G_ClientNumbersFromString(name, pids, MAX_CLIENTS)) != 1) { G_MatchOnePlayer(pids, found, err, sizeof(err)); ADMP(va("^/disorient: ^7%s", err)); return qfalse; } vic = &g_entities[pids[0]]; if(!admin_higher(ent, vic)) { ADMP("^/disorient: ^7sorry, but your intended victim has a higher admin level than you do"); return qfalse; } if(!(vic->client->sess.sessionTeam == TEAM_RED || vic->client->sess.sessionTeam == TEAM_BLUE || vic->client->sess.sessionTeam == TEAM_FREE )) { ADMP("^/disorient: ^7player must be on a team"); return qfalse; } if(vic->client->pers.disoriented) { ADMP(va("^/disorient: ^7%s^7 is already disoriented", vic->client->pers.netname)); return qfalse; } vic->client->pers.disoriented = qtrue; AP(va("chat \"^/disorient: ^7%s ^7is disoriented\" -1", vic->client->pers.netname)); CPx(pids[0], va("cp \"%s ^7disoriented you%s%s\"", (ent?ent->client->pers.netname:"^3SERVER CONSOLE"), (*reason) ? " because:\n" : "", (*reason) ? reason : "")); return qtrue; } qboolean G_admin_orient(gentity_t *ent, int skiparg) { int pids[MAX_CLIENTS], found; char name[MAX_NAME_LENGTH], err[MAX_STRING_CHARS]; gentity_t *vic; if(G_SayArgc() < 2+skiparg) { ADMP("^/orient usage: ^7!orient [name|slot#]"); return qfalse; } G_SayArgv(1+skiparg, name, sizeof(name)); //Fix if((found = G_ClientNumbersFromString(name, pids, MAX_CLIENTS)) != 1) { G_MatchOnePlayer(pids, found, err, sizeof(err)); ADMP(va("^/orient: ^7%s", err)); return qfalse; } vic = &g_entities[pids[0]]; if(!vic->client->pers.disoriented) { ADMP(va("^/orient: ^7%s^7 is not currently disoriented", vic->client->pers.netname)); return qfalse; } vic->client->pers.disoriented = qfalse; AP(va("chat \"^/orient: ^7%s ^7is no longer disoriented\" -1", vic->client->pers.netname)); CPx(pids[0], va("cp \"%s ^7oriented you\"", (ent?ent->client->pers.netname:"^3SERVER CONSOLE"))); return qtrue; } qboolean G_admin_slap( gentity_t *ent, int skiparg ) { int pids[MAX_CLIENTS], found, dmg; char name[MAX_NAME_LENGTH], err[MAX_STRING_CHARS]; char *reason; char damage[4]; gentity_t *vic; int soundIndex; //KK-Too many Parameters Check removed. It'll truncate the reason message. if(G_SayArgc() < 2+skiparg) { ADMP("^/slap usage: ^7!slap [name|slot#] [reason] [damage]"); return qfalse; } G_SayArgv(1+skiparg, name, sizeof(name)); G_SayArgv(2+skiparg, damage, sizeof(damage)); dmg = atoi(damage); if(!dmg) { dmg = 25; reason = G_SayConcatArgs(2+skiparg); } else { reason = G_SayConcatArgs(3+skiparg); } if((found = G_ClientNumbersFromString(name, pids, MAX_CLIENTS)) != 1) { G_MatchOnePlayer(pids, found, err, sizeof(err)); ADMP(va("^/slap: ^7%s", err)); return qfalse; } vic = &g_entities[pids[0]]; if(!admin_higher(ent, vic)) { ADMP("^/slap: ^7sorry, but your intended victim has a higher admin level than you do"); return qfalse; } if(!(vic->client->sess.sessionTeam == TEAM_RED || vic->client->sess.sessionTeam == TEAM_BLUE || vic->client->sess.sessionTeam == TEAM_FREE )) { ADMP("^/slap: ^7player must be in the game!"); return qfalse; } //Player Not Alive if( vic->health < 1 ) { //Is Their Body Alive? if(vic->s.eType != ET_INVISIBLE) { //Make 'em a Bloody mess G_Damage(vic, NULL, NULL, NULL, NULL, 500, 0, MOD_UNKNOWN); } //Force Their Butt to Respawn ClientSpawn( vic ); } // Will the Slap Kill them? (Obviously false if we Respawned 'em) if(!(vic->health > dmg )) { vic->health = 1; } else //If it won't kill em...Do the full Damage { vic->health -= dmg; } //KK-OAX Play them the slap sound soundIndex = G_SoundIndex("sound/admin/slap.wav"); G_Sound(vic, CHAN_VOICE, soundIndex ); //Print it to everybody AP(va("chat \"^/slap: ^7%s ^7was slapped\" -1", vic->client->pers.netname)); //CenterPrint it to the Person Being Slapped CPx(pids[0], va("cp \"%s ^7slapped you%s%s\"", (ent?ent->client->pers.netname:"^3SERVER CONSOLE"), (*reason) ? " because:\n" : "", (*reason) ? reason : "")); return qtrue; } //Called Each Time a Warning is Created int G_admin_warn_check( gentity_t *ent ) { char *ip, *guid; int i; int t; int numWarnings = 0; t = trap_RealTime( NULL ); ip = ent->client->pers.ip; //We Don't Want to Count Warnings for the LocalHost if( !*ip ) return 0; guid = ent->client->pers.guid; //Just to make sure...Don't want to crash...Will Figure something better out later if( !*guid ) return 0; //For Each Warning, up to the max number of warnings for( i = 0; i < MAX_ADMIN_WARNINGS && g_admin_warnings[ i ]; i++ ) { // Ignore Expired Warnings if( ( g_admin_warnings[ i ]->expires - t ) < 1 ) continue; //If a warning matches their IP or GUID if( strstr( ip, g_admin_warnings[ i ]->ip ) || strstr( guid, g_admin_warnings[ i ]->guid )) { numWarnings++; } } //If we get here, return the number of warnings; return numWarnings; } qboolean G_admin_warn( gentity_t *ent, int skiparg ) { int pids[MAX_CLIENTS], found; int seconds; char name[ MAX_NAME_LENGTH ], err[MAX_STRING_CHARS]; char *reason; int minargc; char duration[ 32 ]; char s2[ MAX_NAME_LENGTH ]; gentity_t *vic; int totalWarnings; int soundIndex; if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) { minargc = 1 + skiparg; } else { minargc = 2 + skiparg; } if( G_SayArgc() < minargc ) { ADMP( "^3!warn: ^7usage: !warn [name|slot|ip] [reason]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); G_SanitiseString( name, s2, sizeof( s2 ) ); reason = G_SayConcatArgs(2+skiparg); seconds = g_warningExpire.integer; if((found = G_ClientNumbersFromString(name, pids, MAX_CLIENTS)) != 1) { G_MatchOnePlayer(pids, found, err, sizeof(err)); ADMP(va("^/warn: ^7%s", err)); return qfalse; } vic = &g_entities[pids[0]]; if(!admin_higher(ent, vic)) { ADMP("^/slap: ^7sorry, but your intended victim has a higher admin level than you do"); return qfalse; } G_admin_duration( ( seconds ) ? seconds : -1, duration, sizeof( duration ) ); admin_create_warning( ent, vic->client->pers.netname, vic->client->pers.guid, vic->client->pers.ip, seconds, reason ); if( !g_admin.string[ 0 ] ) ADMP( "^3!warn: ^7WARNING g_admin not set, not saving warning to a file\n" ); else admin_writeconfig(); //KK, Use The Check Warnings Deal Here totalWarnings = G_admin_warn_check( vic ); // Play the whistle soundIndex = G_SoundIndex("sound/admin/whistle.wav"); G_GlobalSound( soundIndex ); //First Check to make sure g_maxWarnings isn't a Null Value if( g_maxWarnings.integer ) { //If they have gone over the max number of warnings... if( totalWarnings >= g_maxWarnings.integer ) { //Give them The Boot till the Warning Expires admin_create_ban( ent, vic->client->pers.netname, vic->client->pers.guid, vic->client->pers.ip, seconds, "Too Many Warnings" ); if( g_admin.string[ 0 ] ) admin_writeconfig(); trap_SendServerCommand( pids[ 0 ], va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\"", ( ent ) ? va( "admin:\n%s", ent->client->pers.netname ) : "SERVER", "Too Many Warnings" ) ); trap_DropClient( pids[ 0 ], va( "has been kicked%s^7. reason: %s", "Auto-Admin System", "Too Many Warnings" ) ); return qtrue; } else { //Print it to everybody AP(va("chat \"^/warn: ^7%s ^7was warned\" -1", vic->client->pers.netname)); //CenterPrint it to the Person Being Slapped CPx(pids[0], va("cp \"%s ^7warned you%s%s\"", (ent?ent->client->pers.netname:"^3SERVER CONSOLE"), (*reason) ? " because:\n" : "", (*reason) ? reason : "")); return qtrue; } } else //KK-OAX g_maxWarnings is null or 0 { AP(va("chat \"^/warn: ^7%s ^7was warned\" -1", vic->client->pers.netname)); //CenterPrint it to the Person Being Slapped CPx(pids[0], va("cp \"%s ^7warned you%s%s\"", (ent?ent->client->pers.netname:"^3SERVER CONSOLE"), (*reason) ? " because:\n" : "", (*reason) ? reason : "")); return qtrue; } } qboolean G_admin_shuffle( gentity_t *ent, int skipargs ) { trap_SendConsoleCommand( EXEC_APPEND, "shuffle" ); AP( va( "print \"^3!shuffle: ^7teams shuffled by %s \n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } //KK-OAX End Additions /* ================ G_admin_print This function facilitates the ADMP define. ADMP() is similar to CP except that it prints the message to the server console if ent is not defined. ================ */ void G_admin_print( gentity_t *ent, char *m ) { if( ent ) trap_SendServerCommand( ent - level.gentities, va( "print \"%s\"", m ) ); else { char m2[ MAX_STRING_CHARS ]; if( !trap_Cvar_VariableIntegerValue( "com_ansiColor" ) ) { G_DecolorString( m, m2, sizeof( m2 ) ); trap_Printf( m2 ); } else trap_Printf( m ); } } void G_admin_buffer_begin() { g_bfb[ 0 ] = '\0'; } void G_admin_buffer_end( gentity_t *ent ) { ADMP( g_bfb ); } void G_admin_buffer_print( gentity_t *ent, char *m ) { // 1022 - strlen("print 64 \"\"") - 1 if( strlen( m ) + strlen( g_bfb ) >= 1009 ) { ADMP( g_bfb ); g_bfb[ 0 ] = '\0'; } Q_strcat( g_bfb, sizeof( g_bfb ), m ); } void G_admin_cleanup() { int i = 0; for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { BG_Free( g_admin_levels[ i ] ); g_admin_levels[ i ] = NULL; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { BG_Free( g_admin_admins[ i ] ); g_admin_admins[ i ] = NULL; } for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { BG_Free( g_admin_bans[ i ] ); g_admin_bans[ i ] = NULL; } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { BG_Free( g_admin_commands[ i ] ); g_admin_commands[ i ] = NULL; } } openarena_0.8.8.orig/code/game/g_trigger.c0000644000175000017500000002737611656310264017205 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" void InitTrigger( gentity_t *self ) { if (!VectorCompare (self->s.angles, vec3_origin)) G_SetMovedir (self->s.angles, self->movedir); trap_SetBrushModel( self, self->model ); self->r.contents = CONTENTS_TRIGGER; // replaces the -1 from trap_SetBrushModel self->r.svFlags = SVF_NOCLIENT; } // the wait time has passed, so set back up for another activation void multi_wait( gentity_t *ent ) { ent->nextthink = 0; } // the trigger was just activated // ent->activator should be set to the activator so it can be held through a delay // so wait for the delay time before firing void multi_trigger( gentity_t *ent, gentity_t *activator ) { ent->activator = activator; if ( ent->nextthink ) { return; // can't retrigger until the wait is over } if ( activator->client ) { if ( ( ent->spawnflags & 1 ) && activator->client->sess.sessionTeam != TEAM_RED ) { return; } if ( ( ent->spawnflags & 2 ) && activator->client->sess.sessionTeam != TEAM_BLUE ) { return; } } G_UseTargets (ent, ent->activator); if ( ent->wait > 0 ) { ent->think = multi_wait; ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; } else { // we can't just remove (self) here, because this is a touch function // called while looping through area links... ent->touch = 0; ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEntity; } } void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) { multi_trigger( ent, activator ); } void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) { if( !other->client ) { return; } multi_trigger( self, other ); } /*QUAKED trigger_multiple (.5 .5 .5) ? "wait" : Seconds between triggerings, 0.5 default, -1 = one time only. "random" wait variance, default is 0 Variable sized repeatable trigger. Must be targeted at one or more entities. so, the basic time between firing is a random time between (wait - random) and (wait + random) */ void SP_trigger_multiple( gentity_t *ent ) { G_SpawnFloat( "wait", "0.5", &ent->wait ); G_SpawnFloat( "random", "0", &ent->random ); if ( ent->random >= ent->wait && ent->wait >= 0 ) { ent->random = ent->wait - FRAMETIME; G_Printf( "trigger_multiple has random >= wait\n" ); } ent->touch = Touch_Multi; ent->use = Use_Multi; InitTrigger( ent ); trap_LinkEntity (ent); } /* ============================================================================== trigger_always ============================================================================== */ void trigger_always_think( gentity_t *ent ) { G_UseTargets(ent, ent); G_FreeEntity( ent ); } /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) This trigger will always fire. It is activated by the world. */ void SP_trigger_always (gentity_t *ent) { // we must have some delay to make sure our use targets are present ent->nextthink = level.time + 300; ent->think = trigger_always_think; } /* ============================================================================== trigger_push ============================================================================== */ void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { if ( !other->client ) { return; } BG_TouchJumpPad( &other->client->ps, &self->s ); } /* ================= AimAtTarget Calculate origin2 so the target apogee will be hit ================= */ void AimAtTarget( gentity_t *self ) { gentity_t *ent; vec3_t origin; float height, gravity, time, forward; float dist; VectorAdd( self->r.absmin, self->r.absmax, origin ); VectorScale ( origin, 0.5, origin ); ent = G_PickTarget( self->target ); if ( !ent ) { G_FreeEntity( self ); return; } height = ent->s.origin[2] - origin[2]; gravity = g_gravity.value*g_gravityModifier.value; time = sqrt( height / ( .5 * gravity ) ); if ( !time ) { G_FreeEntity( self ); return; } // set s.origin2 to the push velocity VectorSubtract ( ent->s.origin, origin, self->s.origin2 ); self->s.origin2[2] = 0; dist = VectorNormalize( self->s.origin2); forward = dist / time; VectorScale( self->s.origin2, forward, self->s.origin2 ); self->s.origin2[2] = time * gravity; } /*QUAKED trigger_push (.5 .5 .5) ? Must point at a target_position, which will be the apex of the leap. This will be client side predicted, unlike target_push */ void SP_trigger_push( gentity_t *self ) { InitTrigger (self); // unlike other triggers, we need to send this one to the client self->r.svFlags &= ~SVF_NOCLIENT; // make sure the client precaches this sound G_SoundIndex("sound/world/jumppad.wav"); self->s.eType = ET_PUSH_TRIGGER; self->touch = trigger_push_touch; self->think = AimAtTarget; self->nextthink = level.time + FRAMETIME; trap_LinkEntity (self); } void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( !activator->client ) { return; } if ( activator->client->ps.pm_type != PM_NORMAL ) { return; } if ( activator->client->ps.powerups[PW_FLIGHT] ) { return; } VectorCopy (self->s.origin2, activator->client->ps.velocity); // play fly sound every 1.5 seconds if ( activator->fly_sound_debounce_time < level.time ) { activator->fly_sound_debounce_time = level.time + 1500; G_Sound( activator, CHAN_AUTO, self->noise_index ); } } /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad Pushes the activator in the direction.of angle, or towards a target apex. "speed" defaults to 1000 if "bouncepad", play bounce noise instead of windfly */ void SP_target_push( gentity_t *self ) { if (!self->speed) { self->speed = 1000; } G_SetMovedir (self->s.angles, self->s.origin2); VectorScale (self->s.origin2, self->speed, self->s.origin2); if ( self->spawnflags & 1 ) { self->noise_index = G_SoundIndex("sound/world/jumppad.wav"); } else { self->noise_index = G_SoundIndex("sound/misc/windfly.wav"); } if ( self->target ) { VectorCopy( self->s.origin, self->r.absmin ); VectorCopy( self->s.origin, self->r.absmax ); self->think = AimAtTarget; self->nextthink = level.time + FRAMETIME; } self->use = Use_target_push; } /* ============================================================================== trigger_teleport ============================================================================== */ void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { gentity_t *dest; if ( !other->client ) { return; } if ( other->client->ps.pm_type == PM_DEAD ) { return; } // Spectators only? if ( ( self->spawnflags & 1 ) && (other->client->sess.sessionTeam != TEAM_SPECTATOR && other->client->ps.pm_type != PM_SPECTATOR) ) { return; } dest = G_PickTarget( self->target ); if (!dest) { G_Printf ("Couldn't find teleporter destination\n"); return; } TeleportPlayer( other, dest->s.origin, dest->s.angles ); } /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR Allows client side prediction of teleportation events. Must point at a target_position, which will be the teleport destination. If spectator is set, only spectators can use this teleport Spectator teleporters are not normally placed in the editor, but are created automatically near doors to allow spectators to move through them */ void SP_trigger_teleport( gentity_t *self ) { InitTrigger (self); // unlike other triggers, we need to send this one to the client // unless is a spectator trigger if ( self->spawnflags & 1 ) { self->r.svFlags |= SVF_NOCLIENT; } else { self->r.svFlags &= ~SVF_NOCLIENT; } // make sure the client precaches this sound G_SoundIndex("sound/world/jumppad.wav"); self->s.eType = ET_TELEPORT_TRIGGER; self->touch = trigger_teleporter_touch; trap_LinkEntity (self); } /* ============================================================================== trigger_hurt ============================================================================== */ /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF - SILENT NO_PROTECTION SLOW Any entity that touches this will be hurt. It does dmg points of damage each server frame Targeting the trigger will toggle its on / off state. SILENT supresses playing the sound SLOW changes the damage rate to once per second NO_PROTECTION *nothing* stops the damage "dmg" default 5 (whole numbers only) */ void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->r.linked ) { trap_UnlinkEntity( self ); } else { trap_LinkEntity( self ); } } void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { int dflags; if ( !other->takedamage ) { return; } if ( self->timestamp > level.time ) { return; } if ( self->spawnflags & 16 ) { self->timestamp = level.time + 1000; } else { self->timestamp = level.time + FRAMETIME; } // play sound if ( !(self->spawnflags & 4) ) { G_Sound( other, CHAN_AUTO, self->noise_index ); } if (self->spawnflags & 8) dflags = DAMAGE_NO_PROTECTION; else dflags = 0; G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); } void SP_trigger_hurt( gentity_t *self ) { InitTrigger (self); self->noise_index = G_SoundIndex( "sound/world/electro.wav" ); self->touch = hurt_touch; if ( !self->damage ) { self->damage = 5; } self->r.contents = CONTENTS_TRIGGER; self->use = hurt_use; // link in to the world if starting active if ( self->spawnflags & 1 ) { trap_UnlinkEntity (self); } else { trap_LinkEntity (self); } } /* ============================================================================== timer ============================================================================== */ /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON This should be renamed trigger_timer... Repeatedly fires its targets. Can be turned on or off by using. "wait" base time between triggering all targets, default is 1 "random" wait variance, default is 0 so, the basic time between firing is a random time between (wait - random) and (wait + random) */ void func_timer_think( gentity_t *self ) { G_UseTargets (self, self->activator); // set time before next firing self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random ); } void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { self->activator = activator; // if on, turn it off if ( self->nextthink ) { self->nextthink = 0; return; } // turn it on func_timer_think (self); } void SP_func_timer( gentity_t *self ) { G_SpawnFloat( "random", "1", &self->random); G_SpawnFloat( "wait", "1", &self->wait ); self->use = func_timer_use; self->think = func_timer_think; if ( self->random >= self->wait ) { self->random = self->wait - FRAMETIME; G_Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) ); } if ( self->spawnflags & 1 ) { self->nextthink = level.time + FRAMETIME; self->activator = self; } self->r.svFlags = SVF_NOCLIENT; } openarena_0.8.8.orig/code/game/bg_lib.h0000644000175000017500000001171611656310264016446 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // bg_lib.h -- standard C library replacement routines used by code // compiled for the virtual machine // This file is NOT included on native builds #if !defined( BG_LIB_H ) && defined( Q3_VM ) #define BG_LIB_H //Ignore __attribute__ on non-gcc platforms #ifndef __GNUC__ #ifndef __attribute__ #define __attribute__(x) #endif #endif #ifndef NULL #define NULL ((void *)0) #endif typedef int size_t; typedef char * va_list; #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) #define CHAR_BIT 8 /* number of bits in a char */ #define SCHAR_MAX 0x7f /* maximum signed char value */ #define SCHAR_MIN (-SCHAR_MAX - 1) /* minimum signed char value */ #define UCHAR_MAX 0xff /* maximum unsigned char value */ #define SHRT_MAX 0x7fff /* maximum (signed) short value */ #define SHRT_MIN (-SHRT_MAX - 1) /* minimum (signed) short value */ #define USHRT_MAX 0xffff /* maximum unsigned short value */ #define INT_MAX 0x7fffffff /* maximum (signed) int value */ #define INT_MIN (-INT_MAX - 1) /* minimum (signed) int value */ #define UINT_MAX 0xffffffff /* maximum unsigned int value */ #define LONG_MAX 0x7fffffffL /* maximum (signed) long value */ #define LONG_MIN (-LONG_MAX - 1) /* minimum (signed) long value */ #define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */ #define isalnum(c) (isalpha(c) || isdigit(c)) #define isalpha(c) (isupper(c) || islower(c)) #define isascii(c) ((c) > 0 && (c) <= 0x7f) #define iscntrl(c) (((c) >= 0) && (((c) <= 0x1f) || ((c) == 0x7f))) #define isdigit(c) ((c) >= '0' && (c) <= '9') #define isgraph(c) ((c) != ' ' && isprint(c)) #define islower(c) ((c) >= 'a' && (c) <= 'z') #define isprint(c) ((c) >= ' ' && (c) <= '~') #define ispunct(c) (((c) > ' ' && (c) <= '~') && !isalnum(c)) #define isspace(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || (c) == '\r' || \ (c) == '\t' || (c) == '\v') #define isupper(c) ((c) >= 'A' && (c) <= 'Z') #define isxdigit(c) (isxupper(c) || isxlower(c)) #define isxlower(c) (isdigit(c) || (c >= 'a' && c <= 'f')) #define isxupper(c) (isdigit(c) || (c >= 'A' && c <= 'F')) // Misc functions typedef int cmp_t(const void *, const void *); void qsort(void *a, size_t n, size_t es, cmp_t *cmp); void srand( unsigned seed ); int rand( void ); // String functions size_t strlen( const char *string ); char *strcat( char *strDestination, const char *strSource ); char *strcpy( char *strDestination, const char *strSource ); int strcmp( const char *string1, const char *string2 ); char *strchr( const char *string, int c ); char *strrchr(const char *string, int c); char *strstr( const char *string, const char *strCharSet ); char *strncpy( char *strDest, const char *strSource, size_t count ); int tolower( int c ); int toupper( int c ); double atof( const char *string ); double _atof( const char **stringPtr ); double strtod( const char *nptr, const char **endptr ); int atoi( const char *string ); int _atoi( const char **stringPtr ); long strtol( const char *nptr, const char **endptr, int base ); int Q_vsnprintf( char *buffer, size_t length, const char *fmt, va_list argptr ); int Q_snprintf( char *buffer, size_t length, const char *fmt, ... ) __attribute__ ((format (printf, 3, 4))); int sscanf( const char *buffer, const char *fmt, ... ) __attribute__ ((format (scanf, 2, 3))); // Memory functions void *memmove( void *dest, const void *src, size_t count ); void *memset( void *dest, int c, size_t count ); void *memcpy( void *dest, const void *src, size_t count ); // Math functions double ceil( double x ); double floor( double x ); double sqrt( double x ); double sin( double x ); double cos( double x ); double atan2( double y, double x ); double tan( double x ); int abs( int n ); double fabs( double x ); double acos( double x ); #endif // BG_LIB_H openarena_0.8.8.orig/code/game/bg_local.h0000644000175000017500000000437411656310264016774 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_local.h -- local definitions for the bg (both games) files #define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes #define STEPSIZE 18 #define JUMP_VELOCITY 270 #define TIMER_LAND 130 #define TIMER_GESTURE (34*66+50) #define OVERCLIP 1.001f // all of the locals will be zeroed before each // pmove, just to make damn sure we don't have // any differences when running on client or server typedef struct { vec3_t forward, right, up; float frametime; int msec; qboolean walking; qboolean groundPlane; trace_t groundTrace; float impactSpeed; vec3_t previous_origin; vec3_t previous_velocity; int previous_waterlevel; } pml_t; extern pmove_t *pm; extern pml_t pml; // movement parameters extern float pm_stopspeed; extern float pm_duckScale; extern float pm_swimScale; extern float pm_wadeScale; extern float pm_accelerate; extern float pm_airaccelerate; extern float pm_wateraccelerate; extern float pm_flyaccelerate; extern float pm_friction; extern float pm_waterfriction; extern float pm_flightfriction; extern int c_pmove; void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ); void PM_AddTouchEnt( int entityNum ); void PM_AddEvent( int newEvent ); qboolean PM_SlideMove( qboolean gravity ); void PM_StepSlideMove( qboolean gravity ); openarena_0.8.8.orig/code/game/ai_team.c0000644000175000017500000021666211656310264016631 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_team.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_team.c $ * *****************************************************************************/ #include "g_local.h" #include "../botlib/botlib.h" #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" #include "ai_vcmd.h" #include "match.h" // for the voice chats #include "../../ui/menudef.h" //ctf task preferences for a client typedef struct bot_ctftaskpreference_s { char name[36]; int preference; } bot_ctftaskpreference_t; bot_ctftaskpreference_t ctftaskpreferences[MAX_CLIENTS]; /* ================== BotValidTeamLeader ================== */ int BotValidTeamLeader(bot_state_t *bs) { if (!strlen(bs->teamleader)) return qfalse; if (ClientFromName(bs->teamleader) == -1) return qfalse; return qtrue; } /* ================== BotNumTeamMates ================== */ int BotNumTeamMates(bot_state_t *bs) { int i, numplayers; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numplayers = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { numplayers++; } } return numplayers; } /* ================== BotClientTravelTimeToGoal ================== */ int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) { playerState_t ps; int areanum; BotAI_GetClientState(client, &ps); areanum = BotPointAreaNum(ps.origin); if (!areanum) return 1; return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); } /* ================== BotSortTeamMatesByBaseTravelTime ================== */ int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxteammates) { int i, j, k, numteammates, traveltime; char buf[MAX_INFO_STRING]; static int maxclients; int traveltimes[MAX_CLIENTS]; bot_goal_t *goal = NULL; if (gametype == GT_CTF || gametype == GT_1FCTF || gametype == GT_CTF_ELIMINATION) { if (BotTeam(bs) == TEAM_RED) goal = &ctf_redflag; else goal = &ctf_blueflag; } else { if (BotTeam(bs) == TEAM_RED) goal = &redobelisk; else goal = &blueobelisk; } if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { // traveltime = BotClientTravelTimeToGoal(i, goal); // for (j = 0; j < numteammates; j++) { if (traveltime < traveltimes[j]) { for (k = numteammates; k > j; k--) { traveltimes[k] = traveltimes[k-1]; teammates[k] = teammates[k-1]; } break; } } traveltimes[j] = traveltime; teammates[j] = i; numteammates++; if (numteammates >= maxteammates) break; } } return numteammates; } /* ================== BotSortTeamMatesByReletiveTravelTime2ddA For Double Domination ================== */ int BotSortTeamMatesByRelativeTravelTime2ddA(bot_state_t *bs, int *teammates, int maxteammates) { int i, j, k, numteammates; double traveltime, traveltime2b; char buf[MAX_INFO_STRING]; static int maxclients; double traveltimes[MAX_CLIENTS]; //int traveltimes2b[MAX_CLIENTS]; bot_goal_t *goalA = &ctf_redflag; bot_goal_t *goalB = &ctf_blueflag; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; if (BotSameTeam(bs, i)) { traveltime = (double)BotClientTravelTimeToGoal(i, goalA); traveltime2b = (double)BotClientTravelTimeToGoal(i, goalB); traveltime = traveltime/traveltime2b; for (j = 0; j < numteammates; j++) { if (traveltime < traveltimes[j]) { for (k = numteammates; k > j; k--) { traveltimes[k] = traveltimes[k-1]; teammates[k] = teammates[k-1]; } break; } } traveltimes[j] = traveltime; teammates[j] = i; numteammates++; if (numteammates >= maxteammates) break; } } return numteammates; } /* ================== BotSetTeamMateTaskPreference ================== */ void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference) { char teammatename[MAX_NETNAME]; ctftaskpreferences[teammate].preference = preference; ClientName(teammate, teammatename, sizeof(teammatename)); strcpy(ctftaskpreferences[teammate].name, teammatename); } /* ================== BotGetTeamMateTaskPreference ================== */ int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate) { char teammatename[MAX_NETNAME]; if (!ctftaskpreferences[teammate].preference) return 0; ClientName(teammate, teammatename, sizeof(teammatename)); if (Q_stricmp(teammatename, ctftaskpreferences[teammate].name)) return 0; return ctftaskpreferences[teammate].preference; } /* ================== BotSortTeamMatesByTaskPreference ================== */ int BotSortTeamMatesByTaskPreference(bot_state_t *bs, int *teammates, int numteammates) { int defenders[MAX_CLIENTS], numdefenders; int attackers[MAX_CLIENTS], numattackers; int roamers[MAX_CLIENTS], numroamers; int i, preference; numdefenders = numattackers = numroamers = 0; for (i = 0; i < numteammates; i++) { preference = BotGetTeamMateTaskPreference(bs, teammates[i]); if (preference & TEAMTP_DEFENDER) { defenders[numdefenders++] = teammates[i]; } else if (preference & TEAMTP_ATTACKER) { attackers[numattackers++] = teammates[i]; } else { roamers[numroamers++] = teammates[i]; } } numteammates = 0; //defenders at the front of the list memcpy(&teammates[numteammates], defenders, numdefenders * sizeof(int)); numteammates += numdefenders; //roamers in the middle memcpy(&teammates[numteammates], roamers, numroamers * sizeof(int)); numteammates += numroamers; //attacker in the back of the list memcpy(&teammates[numteammates], attackers, numattackers * sizeof(int)); numteammates += numattackers; return numteammates; } /* ================== BotSayTeamOrders ================== */ void BotSayTeamOrderAlways(bot_state_t *bs, int toclient) { char teamchat[MAX_MESSAGE_SIZE]; char buf[MAX_MESSAGE_SIZE]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //if the bot is talking to itself if (bs->client == toclient) { //don't show the message just put it in the console message queue trap_BotGetChatMessage(bs->cs, buf, sizeof(buf)); ClientName(bs->client, name, sizeof(name)); Com_sprintf(teamchat, sizeof(teamchat), EC"(%s"EC")"EC": %s", name, buf); trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, teamchat); } else { trap_BotEnterChat(bs->cs, toclient, CHAT_TELL); } } /* ================== BotSayTeamOrders ================== */ void BotSayTeamOrder(bot_state_t *bs, int toclient) { #ifdef MISSIONPACK // voice chats only char buf[MAX_MESSAGE_SIZE]; trap_BotGetChatMessage(bs->cs, buf, sizeof(buf)); #else BotSayTeamOrderAlways(bs, toclient); #endif } /* ================== BotVoiceChat ================== */ void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat) { #ifdef MISSIONPACK if (toclient == -1) // voice only say team trap_EA_Command(bs->client, va("vsay_team %s", voicechat)); else // voice only tell single player trap_EA_Command(bs->client, va("vtell %d %s", toclient, voicechat)); #endif } /* ================== BotVoiceChatOnly ================== */ void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat) { #ifdef MISSIONPACK if (toclient == -1) // voice only say team trap_EA_Command(bs->client, va("vosay_team %s", voicechat)); else // voice only tell single player trap_EA_Command(bs->client, va("votell %d %s", toclient, voicechat)); #endif } /* ================== BotSayVoiceTeamOrder ================== */ void BotSayVoiceTeamOrder(bot_state_t *bs, int toclient, char *voicechat) { #ifdef MISSIONPACK BotVoiceChat(bs, toclient, voicechat); #endif } /* ================== BotCTFOrders ================== */ void BotCTFOrders_BothFlagsNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to attack the enemy base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); if (bot_nochat.integer<3) BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); break; } case 3: { //tell the one closest to the base not carrying the flag to accompany the flag carrier if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); if ( bs->flagcarrier != -1 ) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); if (bs->flagcarrier == bs->client) { if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); } else { if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); } } else { // if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); } if (bot_nochat.integer<3)BotSayTeamOrder(bs, other); //tell the one furthest from the the base not carrying the flag to get the enemy flag if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); if (bot_nochat.integer<3)BotSayTeamOrder(bs, other); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, other, VOICECHAT_RETURNFLAG); break; } default: { defenders = (int) (float) numteammates * 0.4 + 0.5; if (defenders > 4) defenders = 4; attackers = (int) (float) numteammates * 0.5 + 0.5; if (attackers > 5) attackers = 5; if (bs->flagcarrier != -1) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } // ClientName(teammates[i], name, sizeof(name)); if (bs->flagcarrier == bs->client) { if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWME); } else { if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWFLAGCARRIER); } if (bot_nochat.integer<3)BotSayTeamOrder(bs, teammates[i]); } } else { for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } // ClientName(teammates[i], name, sizeof(name)); if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_GETFLAG); if (bot_nochat.integer<3)BotSayTeamOrder(bs, teammates[i]); } } for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); if (bot_nochat.integer<3)BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); if (bot_nochat.integer<3)BotSayTeamOrder(bs, teammates[numteammates - i - 1]); if (bot_nochat.integer<3)BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_RETURNFLAG); } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_FlagNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; qboolean weAreAttacking; if (bot_nochat.integer>2) return; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); weAreAttacking = qfalse; //In oneway ctf we must all move out of the base (only one strategi, maybe we can also send some to the enemy base to meet the flag carier?) //We must be defending if(g_elimination_ctf_oneway.integer > 0) { for (i = 0; i < numteammates; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_GETFLAG); } return; } //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { // keep one near the base for when the flag is returned ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //keep one near the base for when the flag is returned ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other two get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //keep some people near the base for when the flag is returned defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; attackers = (int) (float) numteammates * 0.6 + 0.5; if (attackers > 6) attackers = 6; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); } // break; } } } else { //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { //both will go for the enemy flag ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //everyone go for the flag ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //keep some people near the base for when the flag is returned defenders = (int) (float) numteammates * 0.2 + 0.5; if (defenders > 2) defenders = 2; attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 7) attackers = 7; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_EnemyFlagNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; if (bot_nochat.integer>2) return; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to defend the base if (teammates[0] == bs->flagcarrier) other = teammates[1]; else other = teammates[0]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); break; } case 3: { //tell the one closest to the base not carrying the flag to defend the base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); //tell the other also to defend the base if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); break; } default: { //60% will defend the base defenders = (int) (float) numteammates * 0.6 + 0.5; if (defenders > 6) defenders = 6; //30% accompanies the flag carrier attackers = (int) (float) numteammates * 0.3 + 0.5; if (attackers > 3) attackers = 3; for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } // if we have a flag carrier if ( bs->flagcarrier != -1 ) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } } else { for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } } // break; } } } /* ================== BotDDorders ================== */ void BotDDorders_Standard(bot_state_t *bs) { int numteammates, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByRelativeTravelTime2ddA(bs, teammates, sizeof(teammates)); switch(numteammates) { case 1: break; /*case 2: { //the one closest to point A will take that ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_takea", name, NULL); BotSayTeamOrder(bs, teammates[0]); //BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_TAKEA); //the other goes for point B ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_takeb", name, NULL); BotSayTeamOrder(bs, teammates[1]); //BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_TAKEB); break; }*/ default: { for(i=0;i2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); weAreAttacking = qfalse; if(g_elimination_ctf_oneway.integer > 0) { //See if we are attacking: if( ( (level.eliminationSides+level.roundNumber)%2 == 0 ) && (BotTeam(bs) == TEAM_RED)) weAreAttacking = qtrue; if(weAreAttacking) { for (i = 0; i < numteammates; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_GETFLAG); } } else { for (i = 0; i < numteammates; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } } return; //Sago: Or the leader will make a counter order. } //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the second one closest to the base will defend the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } else { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others should go for the enemy flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { defenders = (int) (float) numteammates * 0.4 + 0.5; if (defenders > 4) defenders = 4; attackers = (int) (float) numteammates * 0.5 + 0.5; if (attackers > 5) attackers = 5; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders(bot_state_t *bs) { int flagstatus; // if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; // switch(flagstatus) { case 0: BotCTFOrders_BothFlagsAtBase(bs); break; case 1: BotCTFOrders_EnemyFlagNotAtBase(bs); break; case 2: BotCTFOrders_FlagNotAtBase(bs); break; case 3: BotCTFOrders_BothFlagsNotAtBase(bs); break; } } /* ================== BotDDorders ================== */ void BotDDorders(bot_state_t *bs) { BotDDorders_Standard(bs); } /* ================== BotCreateGroup ================== */ void BotCreateGroup(bot_state_t *bs, int *teammates, int groupsize) { char name[MAX_NETNAME], leadername[MAX_NETNAME]; int i; if (bot_nochat.integer>2) return; // the others in the group will follow the teammates[0] ClientName(teammates[0], leadername, sizeof(leadername)); for (i = 1; i < groupsize; i++) { ClientName(teammates[i], name, sizeof(name)); if (teammates[0] == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, leadername, NULL); } BotSayTeamOrderAlways(bs, teammates[i]); } } /* ================== BotTeamOrders FIXME: defend key areas? ================== */ void BotTeamOrders(bot_state_t *bs) { int teammates[MAX_CLIENTS]; int numteammates, i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { teammates[numteammates] = i; numteammates++; } } // switch(numteammates) { case 1: break; case 2: { //nothing special break; } case 3: { //have one follow another and one free roaming BotCreateGroup(bs, teammates, 2); break; } case 4: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 2); //a group of 2 break; } case 5: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 3); //a group of 3 break; } default: { if (numteammates <= 10) { for (i = 0; i < numteammates / 2; i++) { BotCreateGroup(bs, &teammates[i*2], 2); //groups of 2 } } break; } } } /* ================== Bot1FCTFOrders_FlagAtCenter X% defend the base, Y% get the flag ================== */ void Bot1FCTFOrders_FlagAtCenter(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the second one closest to the base will defend the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //50% defend the base defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; //40% get the flag attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } else { //agressive //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others should go for the enemy flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //30% defend the base defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; //60% get the flag attackers = (int) (float) numteammates * 0.6 + 0.5; if (attackers > 6) attackers = 6; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== Bot1FCTFOrders_TeamHasFlag X% towards neutral flag, Y% go towards enemy base and accompany flag carrier if visible ================== */ void Bot1FCTFOrders_TeamHasFlag(bot_state_t *bs) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to attack the enemy base if (teammates[0] == bs->flagcarrier) other = teammates[1]; else other = teammates[0]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_OFFENSE); break; } case 3: { //tell the one closest to the base not carrying the flag to defend the base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); //tell the one furthest from the base not carrying the flag to accompany the flag carrier if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); if ( bs->flagcarrier != -1 ) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); } } else { // BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); } BotSayTeamOrder(bs, other); break; } default: { //30% will defend the base defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; //70% accompanies the flag carrier attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 7) attackers = 7; for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } if (bs->flagcarrier != -1) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } } else { for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } } // break; } } } else { //agressive //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to defend the base if (teammates[0] == bs->flagcarrier) other = teammates[1]; else other = teammates[0]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); break; } case 3: { //tell the one closest to the base not carrying the flag to defend the base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); //tell the one furthest from the base not carrying the flag to accompany the flag carrier if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); ClientName(bs->flagcarrier, carriername, sizeof(carriername)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, other); break; } default: { //20% will defend the base defenders = (int) (float) numteammates * 0.2 + 0.5; if (defenders > 2) defenders = 2; //80% accompanies the flag carrier attackers = (int) (float) numteammates * 0.8 + 0.5; if (attackers > 8) attackers = 8; for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } // break; } } } } /* ================== Bot1FCTFOrders_EnemyHasFlag X% defend the base, Y% towards neutral flag ================== */ void Bot1FCTFOrders_EnemyHasFlag(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //both defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the second one closest to the base will defend the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other will also defend the base ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_DEFEND); break; } default: { //80% will defend the base defenders = (int) (float) numteammates * 0.8 + 0.5; if (defenders > 8) defenders = 8; //10% will try to return the flag attackers = (int) (float) numteammates * 0.1 + 0.5; if (attackers > 1) attackers = 1; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } else { //agressive //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others should go for the enemy flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //70% defend the base defenders = (int) (float) numteammates * 0.7 + 0.5; if (defenders > 7) defenders = 7; //20% try to return the flag attackers = (int) (float) numteammates * 0.2 + 0.5; if (attackers > 2) attackers = 2; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_returnflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== Bot1FCTFOrders_EnemyDroppedFlag X% defend the base, Y% get the flag ================== */ void Bot1FCTFOrders_EnemyDroppedFlag(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the second one closest to the base will defend the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //50% defend the base defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; //40% get the flag attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } else { //agressive //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others should go for the enemy flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //30% defend the base defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; //60% get the flag attackers = (int) (float) numteammates * 0.6 + 0.5; if (attackers > 6) attackers = 6; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== Bot1FCTFOrders ================== */ void Bot1FCTFOrders(bot_state_t *bs) { switch(bs->neutralflagstatus) { case 0: Bot1FCTFOrders_FlagAtCenter(bs); break; case 1: Bot1FCTFOrders_TeamHasFlag(bs); break; case 2: Bot1FCTFOrders_EnemyHasFlag(bs); break; case 3: Bot1FCTFOrders_EnemyDroppedFlag(bs); break; } } /* ================== BotObeliskOrders X% in defence Y% in offence ================== */ void BotObeliskOrders(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will attack the enemy base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the one second closest to the base also defends the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other one attacks the enemy base ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); break; } default: { //50% defend the base defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; //40% attack the enemy base attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); } // break; } } } else { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will attack the enemy base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others attack the enemy base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); break; } default: { //30% defend the base defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; //70% attack the enemy base attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 7) attackers = 7; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_attackenemybase", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); } // break; } } } } /* ================== BotHarvesterOrders X% defend the base, Y% harvest ================== */ void BotHarvesterOrders(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; if (bot_nochat.integer>2) return; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will harvest ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the one second closest to the base also defends the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other one goes harvesting ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); break; } default: { //50% defend the base defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; //40% goes harvesting attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); } // break; } } } else { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will harvest ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others go harvesting ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_OFFENSE); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_OFFENSE); break; } default: { //30% defend the base defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; //70% go harvesting attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 7) attackers = 7; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_harvest", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_OFFENSE); } // break; } } } } /* ================== FindHumanTeamLeader ================== */ int FindHumanTeamLeader(bot_state_t *bs) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if ( g_entities[i].inuse ) { // if this player is not a bot if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { // if this player is ok with being the leader if (!notleader[i]) { // if this player is on the same team if ( BotSameTeam(bs, i) ) { ClientName(i, bs->teamleader, sizeof(bs->teamleader)); // if not yet ordered to do anything if ( !BotSetLastOrderedTask(bs) ) { // go on defense by default if (bot_nochat.integer<3)BotVoiceChat_Defend(bs, i, SAY_TELL); } return qtrue; } } } } } return qfalse; } int lastRoundNumber; //used to give new orders every round /* ================== BotTeamAI ================== */ void BotTeamAI(bot_state_t *bs) { int numteammates; char netname[MAX_NETNAME]; if (bot_nochat.integer>2) return; // if ( gametype < GT_TEAM || g_ffa_gt == 1 ) return; // make sure we've got a valid team leader if (!BotValidTeamLeader(bs)) { // if (!FindHumanTeamLeader(bs)) { // if (!bs->askteamleader_time && !bs->becometeamleader_time) { if (bs->entergame_time + 10 > FloatTime()) { bs->askteamleader_time = FloatTime() + 5 + random() * 10; } else { bs->becometeamleader_time = FloatTime() + 5 + random() * 10; } } if (bs->askteamleader_time && bs->askteamleader_time < FloatTime()) { // if asked for a team leader and no response BotAI_BotInitialChat(bs, "whoisteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); bs->askteamleader_time = 0; bs->becometeamleader_time = FloatTime() + 8 + random() * 10; } if (bs->becometeamleader_time && bs->becometeamleader_time < FloatTime()) { BotAI_BotInitialChat(bs, "iamteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotSayVoiceTeamOrder(bs, -1, VOICECHAT_STARTLEADER); ClientName(bs->client, netname, sizeof(netname)); strncpy(bs->teamleader, netname, sizeof(bs->teamleader)); bs->teamleader[sizeof(bs->teamleader)-1] = '\0'; bs->becometeamleader_time = 0; } return; } } bs->askteamleader_time = 0; bs->becometeamleader_time = 0; //return if this bot is NOT the team leader ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; // numteammates = BotNumTeamMates(bs); //give orders switch(gametype) { case GT_TEAM: { if (bs->numteammates != numteammates || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->forceorders = qfalse; } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { BotTeamOrders(bs); //give orders again after 120 seconds bs->teamgiveorders_time = FloatTime() + 120; } break; } case GT_CTF: case GT_CTF_ELIMINATION: { //if the number of team mates changed or the flag status changed //or someone wants to know what to do if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders || lastRoundNumber != level.roundNumber) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->flagstatuschanged = qfalse; bs->forceorders = qfalse; lastRoundNumber = level.roundNumber; } //if there were no flag captures the last 3 minutes if (bs->lastflagcapture_time < FloatTime() - 240) { bs->lastflagcapture_time = FloatTime(); //randomly change the CTF strategy if (random() < 0.4) { bs->ctfstrategy ^= CTFS_AGRESSIVE; bs->teamgiveorders_time = FloatTime(); } } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 3) { BotCTFOrders(bs); // bs->teamgiveorders_time = 0; } break; } case GT_DOUBLE_D: { //if the number of team mates changed or the domination point status changed //or someone wants to know what to do if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->flagstatuschanged = qfalse; bs->forceorders = qfalse; } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 3) { BotDDorders(bs); // bs->teamgiveorders_time = 0; } break; } case GT_1FCTF: { if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->flagstatuschanged = qfalse; bs->forceorders = qfalse; } //if there were no flag captures the last 4 minutes if (bs->lastflagcapture_time < FloatTime() - 240) { bs->lastflagcapture_time = FloatTime(); //randomly change the CTF strategy if (random() < 0.4) { bs->ctfstrategy ^= CTFS_AGRESSIVE; bs->teamgiveorders_time = FloatTime(); } } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 2) { Bot1FCTFOrders(bs); // bs->teamgiveorders_time = 0; } break; } case GT_OBELISK: { if (bs->numteammates != numteammates || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->forceorders = qfalse; } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { BotObeliskOrders(bs); //give orders again after 30 seconds bs->teamgiveorders_time = FloatTime() + 30; } break; } case GT_HARVESTER: { if (bs->numteammates != numteammates || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->forceorders = qfalse; } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { BotHarvesterOrders(bs); //give orders again after 30 seconds bs->teamgiveorders_time = FloatTime() + 30; } break; } } } openarena_0.8.8.orig/code/game/g_missile.c0000644000175000017500000005765211656310264017207 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" #define MISSILE_PRESTEP_TIME 50 /* ================ G_BounceMissile ================ */ void G_BounceMissile( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); if ( ent->s.eFlags & EF_BOUNCE_HALF ) { VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta ); // check for stop if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) { G_SetOrigin( ent, trace->endpos ); ent->s.time = level.time / 4; return; } } VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin); VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } /* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; ent->s.eType = ET_GENERAL; G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue; // splash damage if ( ent->splashDamage ) { if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; g_entities[ent->r.ownerNum].client->accuracy[ent->s.weapon][1]++; } } trap_LinkEntity( ent ); } /* ================ ProximityMine_Explode ================ */ static void ProximityMine_Explode( gentity_t *mine ) { G_ExplodeMissile( mine ); // if the prox mine has a trigger free it if (mine->activator) { G_FreeEntity(mine->activator); mine->activator = NULL; } } /* ================ ProximityMine_Die ================ */ static void ProximityMine_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) { ent->think = ProximityMine_Explode; ent->nextthink = level.time + 1; } /* ================ ProximityMine_Trigger ================ */ void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ) { vec3_t v; gentity_t *mine; if( !other->client ) { return; } // trigger is a cube, do a distance test now to act as if it's a sphere VectorSubtract( trigger->s.pos.trBase, other->s.pos.trBase, v ); if( VectorLength( v ) > trigger->parent->splashRadius ) { return; } if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { // don't trigger same team mines if (trigger->parent->s.generic1 == other->client->sess.sessionTeam) { return; } } // ok, now check for ability to damage so we don't get triggered thru walls, closed doors, etc... if( !CanDamage( other, trigger->s.pos.trBase ) ) { return; } // trigger the mine! mine = trigger->parent; mine->s.loopSound = 0; G_AddEvent( mine, EV_PROXIMITY_MINE_TRIGGER, 0 ); mine->nextthink = level.time + 500; G_FreeEntity( trigger ); } /* ================ ProximityMine_Activate ================ */ static void ProximityMine_Activate( gentity_t *ent ) { gentity_t *trigger; float r; vec3_t v1; gentity_t *flag; char *c; qboolean nearFlag = qfalse; // find the flag switch (ent->s.generic1) { case TEAM_RED: c = "team_CTF_redflag"; break; case TEAM_BLUE: c = "team_CTF_blueflag"; break; default: c = NULL; } if(c) { flag = NULL; while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) { if (!(flag->flags & FL_DROPPED_ITEM)) break; } if(flag) { VectorSubtract(ent->r.currentOrigin,flag->r.currentOrigin , v1); if(VectorLength(v1) < 500) nearFlag = qtrue; } } ent->think = ProximityMine_Explode; if( nearFlag) ent->nextthink = level.time + g_proxMineTimeout.integer/15; else ent->nextthink = level.time + g_proxMineTimeout.integer; ent->takedamage = qtrue; ent->health = 1; ent->die = ProximityMine_Die; ent->s.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav" ); // build the proximity trigger trigger = G_Spawn (); trigger->classname = "proxmine_trigger"; r = ent->splashRadius; VectorSet( trigger->r.mins, -r, -r, -r ); VectorSet( trigger->r.maxs, r, r, r ); G_SetOrigin( trigger, ent->s.pos.trBase ); trigger->parent = ent; trigger->r.contents = CONTENTS_TRIGGER; trigger->touch = ProximityMine_Trigger; trap_LinkEntity (trigger); // set pointer to trigger so the entity can be freed when the mine explodes ent->activator = trigger; } /* ================ ProximityMine_ExplodeOnPlayer ================ */ static void ProximityMine_ExplodeOnPlayer( gentity_t *mine ) { gentity_t *player; player = mine->enemy; player->client->ps.eFlags &= ~EF_TICKING; if ( player->client->invulnerabilityTime > level.time ) { G_Damage( player, mine->parent, mine->parent, vec3_origin, mine->s.origin, 1000, DAMAGE_NO_KNOCKBACK, MOD_JUICED ); player->client->invulnerabilityTime = 0; G_TempEntity( player->client->ps.origin, EV_JUICED ); } else { G_SetOrigin( mine, player->s.pos.trBase ); // make sure the explosion gets to the client mine->r.svFlags &= ~SVF_NOCLIENT; mine->splashMethodOfDeath = MOD_PROXIMITY_MINE; G_ExplodeMissile( mine ); } } /* ================ ProximityMine_Player ================ */ static void ProximityMine_Player( gentity_t *mine, gentity_t *player ) { if( mine->s.eFlags & EF_NODRAW ) { return; } G_AddEvent( mine, EV_PROXIMITY_MINE_STICK, 0 ); if( player->s.eFlags & EF_TICKING ) { player->activator->splashDamage += mine->splashDamage; player->activator->splashRadius *= 1.50; mine->think = G_FreeEntity; mine->nextthink = level.time; return; } player->client->ps.eFlags |= EF_TICKING; player->activator = mine; mine->s.eFlags |= EF_NODRAW; mine->r.svFlags |= SVF_NOCLIENT; mine->s.pos.trType = TR_LINEAR; VectorClear( mine->s.pos.trDelta ); mine->enemy = player; mine->think = ProximityMine_ExplodeOnPlayer; if ( player->client->invulnerabilityTime > level.time ) { mine->nextthink = level.time + 2 * 1000; } else { mine->nextthink = level.time + 10 * 1000; } } /* *================= *ProximityMine_RemoveAll *================= */ void ProximityMine_RemoveAll() { gentity_t *mine; mine = NULL; while ((mine = G_Find (mine, FOFS(classname), "prox mine")) != NULL) { mine->think = ProximityMine_Explode; mine->nextthink = level.time + 1; } } /* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; vec3_t forward, impactpoint, bouncedir; int eFlags; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if ( other->takedamage ) { if ( ent->s.weapon != WP_PROX_LAUNCHER ) { if ( other->client && other->client->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; g_entities[ent->r.ownerNum].client->accuracy[ent->s.weapon][1]++; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); return; } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; g_entities[ent->r.ownerNum].client->accuracy[ent->s.weapon][1]++; } } } trap_LinkEntity( ent ); } /* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } } // if the prox mine wasn't yet outside the player body if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { ent->count = 1; } } // check think function after bouncing G_RunThink( ent ); } //============================================================================= /* ================= fire_plasma ================= */ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "plasma"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PLASMAGUN; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 20; bolt->splashDamage = 15; bolt->splashRadius = 20; bolt->methodOfDeath = MOD_PLASMA; bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 2000, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_grenade ================= */ gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "grenade"; bolt->nextthink = level.time + 2500; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE_LAUNCHER; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 150; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 700, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_bfg ================= */ gentity_t *fire_bfg (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "bfg"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_BFG; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 120; bolt->methodOfDeath = MOD_BFG; bolt->splashMethodOfDeath = MOD_BFG_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 2000, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_rocket ================= */ gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "rocket"; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_ROCKET_LAUNCHER; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 120; bolt->methodOfDeath = MOD_ROCKET; bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 900, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } /* ================= fire_grapple ================= */ gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *hook; //unlagged - grapple int hooktime; //unlagged - grapple VectorNormalize (dir); hook = G_Spawn(); hook->classname = "hook"; hook->nextthink = level.time + 10000; hook->think = Weapon_HookFree; hook->s.eType = ET_MISSILE; hook->r.svFlags = SVF_USE_CURRENT_ORIGIN; hook->s.weapon = WP_GRAPPLING_HOOK; hook->r.ownerNum = self->s.number; hook->methodOfDeath = MOD_GRAPPLE; hook->clipmask = MASK_SHOT; hook->parent = self; hook->target_ent = NULL; //unlagged - grapple // we might want this later hook->s.otherEntityNum = self->s.number; // setting the projectile base time back makes the hook's first // step larger if ( self->client ) { hooktime = self->client->pers.cmd.serverTime + 50; } else { hooktime = level.time - MISSILE_PRESTEP_TIME; } hook->s.pos.trTime = hooktime; //unlagged - grapple hook->s.pos.trType = TR_LINEAR; //unlagged - grapple //hook->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame //unlagged - grapple hook->s.otherEntityNum = self->s.number; // use to match beam in client VectorCopy( start, hook->s.pos.trBase ); VectorScale( dir, 800, hook->s.pos.trDelta ); SnapVector( hook->s.pos.trDelta ); // save net bandwidth VectorCopy (start, hook->r.currentOrigin); self->client->hook = hook; return hook; } /* ================= fire_nail ================= */ #define NAILGUN_SPREAD 500 gentity_t *fire_nail( gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up ) { gentity_t *bolt; vec3_t dir; vec3_t end; float r, u, scale; bolt = G_Spawn(); bolt->classname = "nail"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_NAILGUN; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 20; bolt->methodOfDeath = MOD_NAIL; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); r = random() * M_PI * 2.0f; u = sin(r) * crandom() * NAILGUN_SPREAD * 16; r = cos(r) * crandom() * NAILGUN_SPREAD * 16; VectorMA( start, 8192 * 16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract( end, start, dir ); VectorNormalize( dir ); scale = 555 + random() * 1800; VectorScale( dir, scale, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); VectorCopy( start, bolt->r.currentOrigin ); return bolt; } /* ================= fire_prox ================= */ gentity_t *fire_prox( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "prox mine"; bolt->nextthink = level.time + 3000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PROX_LAUNCHER; bolt->s.eFlags = 0; bolt->r.ownerNum = self->s.number; //unlagged - projectile nudge // we'll need this for nudging projectiles later bolt->s.otherEntityNum = self->s.number; //unlagged - projectile nudge bolt->parent = self; bolt->damage = 0; bolt->splashDamage = 100; bolt->splashRadius = 150; bolt->methodOfDeath = MOD_PROXIMITY_MINE; bolt->splashMethodOfDeath = MOD_PROXIMITY_MINE; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; // count is used to check if the prox mine left the player bbox // if count == 1 then the prox mine left the player bbox and can attack to it bolt->count = 0; //FIXME: we prolly wanna abuse another field bolt->s.generic1 = self->client->sess.sessionTeam; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 700, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } openarena_0.8.8.orig/code/game/g_misc.c0000644000175000017500000003246311656310264016466 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_misc.c #include "g_local.h" /*QUAKED func_group (0 0 0) ? Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities. */ /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay. */ void SP_info_camp( gentity_t *self ) { G_SetOrigin( self, self->s.origin ); } /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay. */ void SP_info_null( gentity_t *self ) { G_FreeEntity( self ); } /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for in-game calculation, like jumppad targets. target_position does the same thing */ void SP_info_notnull( gentity_t *self ){ G_SetOrigin( self, self->s.origin ); } /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear Non-displayed light. "light" overrides the default 300 intensity. Linear checbox gives linear falloff instead of inverse square Lights pointed at a target will be spotlights. "radius" overrides the default 64 unit radius of a spotlight at the target point. */ void SP_light( gentity_t *self ) { G_FreeEntity( self ); } /* ================================================================================= TELEPORTERS ================================================================================= */ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) { gentity_t *tent; qboolean noAngles; noAngles = (angles[0] > 999999.0); // use temp events at source and destination to prevent the effect // from getting dropped by a second player event if ( player->client->sess.sessionTeam != TEAM_SPECTATOR && player->client->ps.pm_type != PM_SPECTATOR) { tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = player->s.clientNum; tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = player->s.clientNum; } // unlink to make sure it can't possibly interfere with G_KillBox trap_UnlinkEntity (player); VectorCopy ( origin, player->client->ps.origin ); player->client->ps.origin[2] += 1; if (!noAngles) { // spit the player out AngleVectors( angles, player->client->ps.velocity, NULL, NULL ); VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity ); player->client->ps.pm_time = 160; // hold time player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; // set angles SetClientViewAngle(player, angles); } // toggle the teleport bit so the client knows to not lerp player->client->ps.eFlags ^= EF_TELEPORT_BIT; //unlagged - backward reconciliation #3 // we don't want players being backward-reconciled back through teleporters G_ResetHistory( player ); //unlagged - backward reconciliation #3 // kill anything at the destination if ( player->client->sess.sessionTeam != TEAM_SPECTATOR && player->client->ps.pm_type != PM_SPECTATOR ) { G_KillBox (player); } // save results of pmove BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue ); // use the precise origin for linking VectorCopy( player->client->ps.origin, player->r.currentOrigin ); if ( player->client->sess.sessionTeam != TEAM_SPECTATOR && player->client->ps.pm_type != PM_SPECTATOR ) { trap_LinkEntity (player); } } /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) Point teleporters at these. Now that we don't have teleport destination pads, this is just an info_notnull */ void SP_misc_teleporter_dest( gentity_t *ent ) { } //=========================================================== /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16) "model" arbitrary .md3 file to display */ void SP_misc_model( gentity_t *ent ) { #if 0 ent->s.modelindex = G_ModelIndex( ent->model ); VectorSet (ent->mins, -16, -16, -16); VectorSet (ent->maxs, 16, 16, 16); trap_LinkEntity (ent); G_SetOrigin( ent, ent->s.origin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); #else G_FreeEntity( ent ); #endif } //=========================================================== void locateCamera( gentity_t *ent ) { vec3_t dir; gentity_t *target; gentity_t *owner; owner = G_PickTarget( ent->target ); if ( !owner ) { G_Printf( "Couldn't find target for misc_partal_surface\n" ); G_FreeEntity( ent ); return; } ent->r.ownerNum = owner->s.number; // frame holds the rotate speed if ( owner->spawnflags & 1 ) { ent->s.frame = 25; } else if ( owner->spawnflags & 2 ) { ent->s.frame = 75; } // swing camera ? if ( owner->spawnflags & 4 ) { // set to 0 for no rotation at all ent->s.powerups = 0; } else { ent->s.powerups = 1; } // clientNum holds the rotate offset ent->s.clientNum = owner->s.clientNum; VectorCopy( owner->s.origin, ent->s.origin2 ); // see if the portal_camera has a target target = G_PickTarget( owner->target ); if ( target ) { VectorSubtract( target->s.origin, owner->s.origin, dir ); VectorNormalize( dir ); } else { G_SetMovedir( owner->s.angles, dir ); } ent->s.eventParm = DirToByte( dir ); } /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8) The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted. This must be within 64 world units of the surface! */ void SP_misc_portal_surface(gentity_t *ent) { VectorClear( ent->r.mins ); VectorClear( ent->r.maxs ); trap_LinkEntity (ent); ent->r.svFlags = SVF_PORTAL; ent->s.eType = ET_PORTAL; if ( !ent->target ) { VectorCopy( ent->s.origin, ent->s.origin2 ); } else { ent->think = locateCamera; ent->nextthink = level.time + 100; } } /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing The target for a misc_portal_director. You can set either angles or target another entity to determine the direction of view. "roll" an angle modifier to orient the camera around the target vector; */ void SP_misc_portal_camera(gentity_t *ent) { float roll; VectorClear( ent->r.mins ); VectorClear( ent->r.maxs ); trap_LinkEntity (ent); G_SpawnFloat( "roll", "0", &roll ); ent->s.clientNum = roll/360.0 * 256; } /* ====================================================================== SHOOTERS ====================================================================== */ void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) { vec3_t dir; float deg; vec3_t up, right; // see if we have a target if ( ent->enemy ) { VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir ); VectorNormalize( dir ); } else { VectorCopy( ent->movedir, dir ); } // randomize a bit PerpendicularVector( up, dir ); CrossProduct( up, dir, right ); deg = crandom() * ent->random; VectorMA( dir, deg, up, dir ); deg = crandom() * ent->random; VectorMA( dir, deg, right, dir ); VectorNormalize( dir ); switch ( ent->s.weapon ) { case WP_GRENADE_LAUNCHER: fire_grenade( ent, ent->s.origin, dir ); break; case WP_ROCKET_LAUNCHER: fire_rocket( ent, ent->s.origin, dir ); break; case WP_PLASMAGUN: fire_plasma( ent, ent->s.origin, dir ); break; } G_AddEvent( ent, EV_FIRE_WEAPON, 0 ); } static void InitShooter_Finish( gentity_t *ent ) { ent->enemy = G_PickTarget( ent->target ); ent->think = 0; ent->nextthink = 0; } void InitShooter( gentity_t *ent, int weapon ) { ent->use = Use_Shooter; ent->s.weapon = weapon; RegisterItem( BG_FindItemForWeapon( weapon ) ); G_SetMovedir( ent->s.angles, ent->movedir ); if ( !ent->random ) { ent->random = 1.0; } ent->random = sin( M_PI * ent->random / 180 ); // target might be a moving object, so we can't set movedir for it if ( ent->target ) { ent->think = InitShooter_Finish; ent->nextthink = level.time + 500; } trap_LinkEntity( ent ); } /*QUAKED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_rocket( gentity_t *ent ) { InitShooter( ent, WP_ROCKET_LAUNCHER ); } /*QUAKED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" is the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_plasma( gentity_t *ent ) { InitShooter( ent, WP_PLASMAGUN); } /*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" is the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_grenade( gentity_t *ent ) { InitShooter( ent, WP_GRENADE_LAUNCHER); } static void PortalDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod) { G_FreeEntity( self ); //FIXME do something more interesting } void DropPortalDestination( gentity_t *player ) { gentity_t *ent; vec3_t snapped; // create the portal destination ent = G_Spawn(); ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_exit.md3" ); VectorCopy( player->s.pos.trBase, snapped ); SnapVector( snapped ); G_SetOrigin( ent, snapped ); VectorCopy( player->r.mins, ent->r.mins ); VectorCopy( player->r.maxs, ent->r.maxs ); ent->classname = "hi_portal destination"; ent->s.pos.trType = TR_STATIONARY; ent->r.contents = CONTENTS_CORPSE; ent->takedamage = qtrue; ent->health = 200; ent->die = PortalDie; VectorCopy( player->s.apos.trBase, ent->s.angles ); ent->think = G_FreeEntity; ent->nextthink = level.time + 2 * 60 * 1000; trap_LinkEntity( ent ); player->client->portalID = ++level.portalSequence; ent->count = player->client->portalID; // give the item back so they can drop the source now player->client->ps.stats[STAT_HOLDABLE_ITEM] = BG_FindItem( "Portal" ) - bg_itemlist; } static void PortalTouch( gentity_t *self, gentity_t *other, trace_t *trace) { gentity_t *destination; // see if we will even let other try to use it if( other->health <= 0 ) { return; } if( !other->client ) { return; } // if( other->client->ps.persistant[PERS_TEAM] != self->spawnflags ) { // return; // } if ( other->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Drop_Item( other, BG_FindItemForPowerup( PW_NEUTRALFLAG ), 0 ); other->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( other->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Drop_Item( other, BG_FindItemForPowerup( PW_REDFLAG ), 0 ); other->client->ps.powerups[PW_REDFLAG] = 0; } else if ( other->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Drop_Item( other, BG_FindItemForPowerup( PW_BLUEFLAG ), 0 ); other->client->ps.powerups[PW_BLUEFLAG] = 0; } // find the destination destination = NULL; while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) { if( destination->count == self->count ) { break; } } // if there is not one, die! if( !destination ) { if( self->pos1[0] || self->pos1[1] || self->pos1[2] ) { TeleportPlayer( other, self->pos1, self->s.angles ); } G_Damage( other, other, other, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG ); return; } TeleportPlayer( other, destination->s.pos.trBase, destination->s.angles ); } static void PortalEnable( gentity_t *self ) { self->touch = PortalTouch; self->think = G_FreeEntity; self->nextthink = level.time + 2 * 60 * 1000; } void DropPortalSource( gentity_t *player ) { gentity_t *ent; gentity_t *destination; vec3_t snapped; // create the portal source ent = G_Spawn(); ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_enter.md3" ); VectorCopy( player->s.pos.trBase, snapped ); SnapVector( snapped ); G_SetOrigin( ent, snapped ); VectorCopy( player->r.mins, ent->r.mins ); VectorCopy( player->r.maxs, ent->r.maxs ); ent->classname = "hi_portal source"; ent->s.pos.trType = TR_STATIONARY; ent->r.contents = CONTENTS_CORPSE | CONTENTS_TRIGGER; ent->takedamage = qtrue; ent->health = 200; ent->die = PortalDie; trap_LinkEntity( ent ); ent->count = player->client->portalID; player->client->portalID = 0; // ent->spawnflags = player->client->ps.persistant[PERS_TEAM]; ent->nextthink = level.time + 1000; ent->think = PortalEnable; // find the destination destination = NULL; while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) { if( destination->count == ent->count ) { VectorCopy( destination->s.pos.trBase, ent->pos1 ); break; } } } openarena_0.8.8.orig/code/game/ai_main.c0000644000175000017500000013344611656310264016625 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_main.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_main.c $ * *****************************************************************************/ #include "g_local.h" #include "../qcommon/q_shared.h" #include "../botlib/botlib.h" //bot lib interface #include "../botlib/be_aas.h" #include "../botlib/be_ea.h" #include "../botlib/be_ai_char.h" #include "../botlib/be_ai_chat.h" #include "../botlib/be_ai_gen.h" #include "../botlib/be_ai_goal.h" #include "../botlib/be_ai_move.h" #include "../botlib/be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_vcmd.h" // #include "chars.h" #include "inv.h" #include "syn.h" #ifndef MAX_PATH #define MAX_PATH 144 #endif //bot states bot_state_t *botstates[MAX_CLIENTS]; //number of bots int numbots; //floating point time float floattime; //time to do a regular update float regularupdate_time; // int bot_interbreed; int bot_interbreedmatchcount; // vmCvar_t bot_thinktime; vmCvar_t bot_memorydump; vmCvar_t bot_saveroutingcache; vmCvar_t bot_pause; vmCvar_t bot_report; vmCvar_t bot_testsolid; vmCvar_t bot_testclusters; vmCvar_t bot_developer; vmCvar_t bot_interbreedchar; vmCvar_t bot_interbreedbots; vmCvar_t bot_interbreedcycle; vmCvar_t bot_interbreedwrite; void ExitLevel( void ); /* ================== BotAI_Print ================== */ void QDECL BotAI_Print(int type, char *fmt, ...) { char str[2048]; va_list ap; va_start(ap, fmt); Q_vsnprintf(str, sizeof(str), fmt, ap); va_end(ap); switch(type) { case PRT_MESSAGE: { G_Printf("%s", str); break; } case PRT_WARNING: { G_Printf( S_COLOR_YELLOW "Warning: %s", str ); break; } case PRT_ERROR: { G_Printf( S_COLOR_RED "Error: %s", str ); break; } case PRT_FATAL: { G_Printf( S_COLOR_RED "Fatal: %s", str ); break; } case PRT_EXIT: { G_Error( S_COLOR_RED "Exit: %s", str ); break; } default: { G_Printf( "unknown print type\n" ); break; } } } /* ================== BotAI_Trace ================== */ void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { trace_t trace; trap_Trace(&trace, start, mins, maxs, end, passent, contentmask); //copy the trace information bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; VectorCopy(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; VectorCopy(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; } /* ================== BotAI_GetClientState ================== */ int BotAI_GetClientState( int clientNum, playerState_t *state ) { gentity_t *ent; ent = &g_entities[clientNum]; if ( !ent->inuse ) { return qfalse; } if ( !ent->client ) { return qfalse; } memcpy( state, &ent->client->ps, sizeof(playerState_t) ); return qtrue; } /* ================== BotAI_GetEntityState ================== */ int BotAI_GetEntityState( int entityNum, entityState_t *state ) { gentity_t *ent; ent = &g_entities[entityNum]; memset( state, 0, sizeof(entityState_t) ); if (!ent->inuse) return qfalse; if (!ent->r.linked) return qfalse; if ( !(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_LMS ||g_instantgib.integer || g_rockets.integer || g_elimination_allgametypes.integer || g_gametype.integer==GT_CTF_ELIMINATION) && (ent->r.svFlags & SVF_NOCLIENT) ) return qfalse; memcpy( state, &ent->s, sizeof(entityState_t) ); return qtrue; } /* ================== BotAI_GetSnapshotEntity ================== */ int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) { int entNum; entNum = trap_BotGetSnapshotEntity( clientNum, sequence ); if ( entNum == -1 ) { memset(state, 0, sizeof(entityState_t)); return -1; } BotAI_GetEntityState( entNum, state ); return sequence + 1; } /* ================== BotAI_BotInitialChat ================== */ void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) { int i, mcontext; va_list ap; char *p; char *vars[MAX_MATCHVARIABLES]; memset(vars, 0, sizeof(vars)); va_start(ap, type); p = va_arg(ap, char *); for (i = 0; i < MAX_MATCHVARIABLES; i++) { if( !p ) { break; } vars[i] = p; p = va_arg(ap, char *); } va_end(ap); mcontext = BotSynonymContext(bs); trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] ); } /* ================== BotTestAAS ================== */ void BotTestAAS(vec3_t origin) { int areanum; aas_areainfo_t info; trap_Cvar_Update(&bot_testsolid); trap_Cvar_Update(&bot_testclusters); if (bot_testsolid.integer) { if (!trap_AAS_Initialized()) return; areanum = BotPointAreaNum(origin); if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area"); else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area"); } else if (bot_testclusters.integer) { if (!trap_AAS_Initialized()) return; areanum = BotPointAreaNum(origin); if (!areanum) BotAI_Print(PRT_MESSAGE, "\r^1Solid! "); else { trap_AAS_AreaInfo(areanum, &info); BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster); } } } /* ================== BotReportStatus ================== */ void BotReportStatus(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char *leader, flagstatus[32]; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(flagstatus, " "); if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (BotCTFCarryingFlag(bs)) { if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); else strcpy(flagstatus, S_COLOR_BLUE"F "); } } else if (gametype == GT_1FCTF) { if (Bot1FCTFCarryingFlag(bs)) { if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); else strcpy(flagstatus, S_COLOR_BLUE"F "); } } else if (gametype == GT_HARVESTER) { if (BotHarvesterCarryingCubes(bs)) { if (BotTeam(bs) == TEAM_RED) Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_RED"%2d", bs->inventory[INVENTORY_REDCUBE]); else Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_BLUE"%2d", bs->inventory[INVENTORY_BLUECUBE]); } } switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus); break; } case LTG_PATROL: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus); break; } case LTG_GETFLAG: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus); break; } case LTG_RUSHBASE: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus); break; } case LTG_RETURNFLAG: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus); break; } case LTG_ATTACKENEMYBASE: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus); break; } case LTG_HARVEST: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus); break; } case LTG_POINTA: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: going for point A\n", netname, leader, flagstatus); break; } case LTG_POINTB: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: going for point B\n", netname, leader, flagstatus); break; } default: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus); break; } } } /* ================== BotTeamplayReport ================== */ void BotTeamplayReport(void) { int i; char buf[MAX_INFO_STRING]; BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) { BotReportStatus(botstates[i]); } } BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) { BotReportStatus(botstates[i]); } } } /* ================== BotSetInfoConfigString ================== */ void BotSetInfoConfigString(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char action[MAX_MESSAGE_SIZE]; char *leader, carrying[32], *cs; bot_goal_t goal; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(carrying, " "); if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) { if (BotCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } else if (gametype == GT_1FCTF) { if (Bot1FCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } else if (gametype == GT_HARVESTER) { if (BotHarvesterCarryingCubes(bs)) { if (BotTeam(bs) == TEAM_RED) Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_REDCUBE]); else Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_BLUECUBE]); } } switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "helping %s", goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "accompanying %s", goalname); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "defending %s", goalname); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "getting item %s", goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "killing %s", goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { Com_sprintf(action, sizeof(action), "camping"); break; } case LTG_PATROL: { Com_sprintf(action, sizeof(action), "patrolling"); break; } case LTG_GETFLAG: { Com_sprintf(action, sizeof(action), "capturing flag"); break; } case LTG_RUSHBASE: { Com_sprintf(action, sizeof(action), "rushing base"); break; } case LTG_RETURNFLAG: { Com_sprintf(action, sizeof(action), "returning flag"); break; } case LTG_ATTACKENEMYBASE: { Com_sprintf(action, sizeof(action), "attacking the enemy base"); break; } case LTG_HARVEST: { Com_sprintf(action, sizeof(action), "harvesting"); break; } case LTG_POINTA: { Com_sprintf(action, sizeof(action), "going for point A"); break; } case LTG_POINTB: { Com_sprintf(action, sizeof(action), "going for point B"); break; } default: { trap_BotGetTopGoal(bs->gs, &goal); trap_BotGoalName(goal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "roaming %s", goalname); break; } } cs = va("l\\%s\\c\\%s\\a\\%s", leader, carrying, action); trap_SetConfigstring (CS_BOTINFO + bs->client, cs); } /* ============== BotUpdateInfoConfigStrings ============== */ void BotUpdateInfoConfigStrings(void) { int i; char buf[MAX_INFO_STRING]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; BotSetInfoConfigString(botstates[i]); } } /* ============== BotInterbreedBots ============== */ void BotInterbreedBots(void) { float ranks[MAX_CLIENTS]; int parent1, parent2, child; int i; // get rankings for all the bots for (i = 0; i < MAX_CLIENTS; i++) { if ( botstates[i] && botstates[i]->inuse ) { ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; } else { ranks[i] = -1; } } if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) { trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs); trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1); } // reset the kills and deaths for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { botstates[i]->num_kills = 0; botstates[i]->num_deaths = 0; } } } /* ============== BotWriteInterbreeded ============== */ void BotWriteInterbreeded(char *filename) { float rank, bestrank; int i, bestbot; bestrank = 0; bestbot = -1; // get the best bot for (i = 0; i < MAX_CLIENTS; i++) { if ( botstates[i] && botstates[i]->inuse ) { rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; } else { rank = -1; } if (rank > bestrank) { bestrank = rank; bestbot = i; } } if (bestbot >= 0) { //write out the new goal fuzzy logic trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename); } } /* ============== BotInterbreedEndMatch add link back into ExitLevel? ============== */ void BotInterbreedEndMatch(void) { if (!bot_interbreed) return; bot_interbreedmatchcount++; if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) { bot_interbreedmatchcount = 0; // trap_Cvar_Update(&bot_interbreedwrite); if (strlen(bot_interbreedwrite.string)) { BotWriteInterbreeded(bot_interbreedwrite.string); trap_Cvar_Set("bot_interbreedwrite", ""); } BotInterbreedBots(); } } /* ============== BotInterbreeding ============== */ void BotInterbreeding(void) { int i; trap_Cvar_Update(&bot_interbreedchar); if (!strlen(bot_interbreedchar.string)) return; //make sure we are in tournament mode if (gametype != GT_TOURNAMENT) { trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT)); ExitLevel(); return; } //shutdown all the bots for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotAIShutdownClient(botstates[i]->client, qfalse); } } //make sure all item weight configs are reloaded and Not shared trap_BotLibVarSet("bot_reloadcharacters", "1"); //add a number of bots using the desired bot character for (i = 0; i < bot_interbreedbots.integer; i++) { trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n", bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) ); } // trap_Cvar_Set("bot_interbreedchar", ""); bot_interbreed = qtrue; } /* ============== BotEntityInfo ============== */ void BotEntityInfo(int entnum, aas_entityinfo_t *info) { trap_AAS_EntityInfo(entnum, info); } /* ============== NumBots ============== */ int NumBots(void) { return numbots; } /* ============== BotTeamLeader ============== */ int BotTeamLeader(bot_state_t *bs) { int leader; leader = ClientFromName(bs->teamleader); if (leader < 0) return qfalse; if (!botstates[leader] || !botstates[leader]->inuse) return qfalse; return qtrue; } /* ============== AngleDifference ============== */ float AngleDifference(float ang1, float ang2) { float diff; diff = ang1 - ang2; if (ang1 > ang2) { if (diff > 180.0) diff -= 360.0; } else { if (diff < -180.0) diff += 360.0; } return diff; } /* ============== BotChangeViewAngle ============== */ float BotChangeViewAngle(float angle, float ideal_angle, float speed) { float move; angle = AngleMod(angle); ideal_angle = AngleMod(ideal_angle); if (angle == ideal_angle) return angle; move = ideal_angle - angle; if (ideal_angle > angle) { if (move > 180.0) move -= 360.0; } else { if (move < -180.0) move += 360.0; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } return AngleMod(angle + move); } /* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; // if (bs->enemy >= 0) { factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); } else { factor = 0.05f; maxchange = 360; } if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { // if (bot_challenge.integer) { //smooth slowdown view model diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); anglespeed = diff * factor; if (anglespeed > maxchange) anglespeed = maxchange; bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], bs->ideal_viewangles[i], anglespeed); } else { //over reaction view model bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); //demping bs->viewanglespeed[i] *= 0.45 * (1 - factor); } //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } //bs->viewangles[PITCH] = 0; if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; //elementary action: view trap_EA_View(bs->client, bs->viewangles); } /* ============== BotInputToUserCommand ============== */ void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) { vec3_t angles, forward, right; short temp; int j; float f, r, u, m; //clear the whole structure memset(ucmd, 0, sizeof(usercmd_t)); //the duration for the user command in milli seconds ucmd->serverTime = time; // if (bi->actionflags & ACTION_DELAYEDJUMP) { bi->actionflags |= ACTION_JUMP; bi->actionflags &= ~ACTION_DELAYEDJUMP; } //set the buttons if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK; if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK; if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK; if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE; if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE; if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING; if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE; if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE; if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG; if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE; if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL; if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME; // ucmd->weapon = bi->weapon; //set the view angles //NOTE: the ucmd->angles are the angles WITHOUT the delta angles ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]); ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]); ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]); //subtract the delta angles for (j = 0; j < 3; j++) { temp = ucmd->angles[j] - delta_angles[j]; /*NOTE: disabled because temp should be mod first if ( j == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) temp = 16000; else if ( temp < -16000 ) temp = -16000; } */ ucmd->angles[j] = temp; } //NOTE: movement is relative to the REAL view angles //get the horizontal forward and right vector //get the pitch in the range [-180, 180] if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH]; else angles[PITCH] = 0; angles[YAW] = bi->viewangles[YAW]; angles[ROLL] = 0; AngleVectors(angles, forward, right, NULL); //bot input speed is in the range [0, 400] bi->speed = bi->speed * 127 / 400; //set the view independent movement f = DotProduct(forward, bi->dir); r = DotProduct(right, bi->dir); u = abs(forward[2]) * bi->dir[2]; m = fabs(f); if (fabs(r) > m) { m = fabs(r); } if (fabs(u) > m) { m = fabs(u); } if (m > 0) { f *= bi->speed / m; r *= bi->speed / m; u *= bi->speed / m; } ucmd->forwardmove = f; ucmd->rightmove = r; ucmd->upmove = u; if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove = 127; if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove = -127; if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove = -127; if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove = 127; //jump/moveup if (bi->actionflags & ACTION_JUMP) ucmd->upmove = 127; //crouch/movedown if (bi->actionflags & ACTION_CROUCH) ucmd->upmove = -127; } /* ============== BotUpdateInput ============== */ void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //change the bot view angles BotChangeViewAngles(bs, (float) elapsed_time / 1000); //retrieve the bot input trap_EA_GetInput(bs->client, (float) time / 1000, &bi); //respawn hack if (bi.actionflags & ACTION_RESPAWN) { if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); } //convert the bot input to a usercmd BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } } /* ============== BotAIRegularUpdate ============== */ void BotAIRegularUpdate(void) { if (regularupdate_time < FloatTime()) { trap_BotUpdateEntityItems(); regularupdate_time = FloatTime() + 0.3; } } /* ============== RemoveColorEscapeSequences ============== */ void RemoveColorEscapeSequences( char *text ) { int i, l; l = 0; for ( i = 0; text[i]; i++ ) { if (Q_IsColorString(&text[i])) { i++; continue; } if (text[i] > 0x7E) continue; text[l++] = text[i]; } text[l] = '\0'; } /* ============== BotAI ============== */ int BotAI(int client, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput(client); // bs = botstates[client]; if (!bs || !bs->inuse) { BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client); return qfalse; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting server commands while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "print")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); } else if (!Q_stricmp(buf, "chat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "tchat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } #ifdef MISSIONPACK else if (!Q_stricmp(buf, "vchat")) { BotVoiceChatCommand(bs, SAY_ALL, args); } else if (!Q_stricmp(buf, "vtchat")) { BotVoiceChatCommand(bs, SAY_TEAM, args); } else if (!Q_stricmp(buf, "vtell")) { BotVoiceChatCommand(bs, SAY_TELL, args); } #endif else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum(bs->origin); //the real AI BotDeathmatchAI(bs, thinktime); //set the weapon selection every AI frame trap_EA_SelectWeapon(bs->client, bs->weaponnum); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; } /* ================== BotScheduleBotThink ================== */ void BotScheduleBotThink(void) { int i, botnum; botnum = 0; for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } //initialize the bot think residual time botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots; botnum++; } } /* ============== BotWriteSessionData ============== */ void BotWriteSessionData(bot_state_t *bs) { const char *s; const char *var; s = va( "%i %i %i %i %i %i %i %i" " %f %f %f" " %f %f %f" " %f %f %f", bs->lastgoal_decisionmaker, bs->lastgoal_ltgtype, bs->lastgoal_teammate, bs->lastgoal_teamgoal.areanum, bs->lastgoal_teamgoal.entitynum, bs->lastgoal_teamgoal.flags, bs->lastgoal_teamgoal.iteminfo, bs->lastgoal_teamgoal.number, bs->lastgoal_teamgoal.origin[0], bs->lastgoal_teamgoal.origin[1], bs->lastgoal_teamgoal.origin[2], bs->lastgoal_teamgoal.mins[0], bs->lastgoal_teamgoal.mins[1], bs->lastgoal_teamgoal.mins[2], bs->lastgoal_teamgoal.maxs[0], bs->lastgoal_teamgoal.maxs[1], bs->lastgoal_teamgoal.maxs[2] ); var = va( "botsession%i", bs->client ); trap_Cvar_Set( var, s ); } /* ============== BotReadSessionData ============== */ void BotReadSessionData(bot_state_t *bs) { char s[MAX_STRING_CHARS]; const char *var; var = va( "botsession%i", bs->client ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); sscanf(s, "%i %i %i %i %i %i %i %i" " %f %f %f" " %f %f %f" " %f %f %f", &bs->lastgoal_decisionmaker, &bs->lastgoal_ltgtype, &bs->lastgoal_teammate, &bs->lastgoal_teamgoal.areanum, &bs->lastgoal_teamgoal.entitynum, &bs->lastgoal_teamgoal.flags, &bs->lastgoal_teamgoal.iteminfo, &bs->lastgoal_teamgoal.number, &bs->lastgoal_teamgoal.origin[0], &bs->lastgoal_teamgoal.origin[1], &bs->lastgoal_teamgoal.origin[2], &bs->lastgoal_teamgoal.mins[0], &bs->lastgoal_teamgoal.mins[1], &bs->lastgoal_teamgoal.mins[2], &bs->lastgoal_teamgoal.maxs[0], &bs->lastgoal_teamgoal.maxs[1], &bs->lastgoal_teamgoal.maxs[2] ); } /* ============== BotAISetupClient ============== */ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; bot_state_t *bs; int errnum; //KK-OAX Changed to Tremulous's BG_Alloc if (!botstates[client]) { if(!BG_CanAlloc(sizeof(bot_state_t))) { //We cannot run BG_Alloc, fail nicely BotAI_Print(PRT_FATAL, "BotAISetupClient: Not enough heap memory\n", client); return qfalse; } botstates[client] = BG_Alloc(sizeof(bot_state_t)); //BG_Allow will succed or terminate } bs = botstates[client]; if (bs && bs->inuse) { BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client); return qfalse; } if (!trap_AAS_Initialized()) { BotAI_Print(PRT_FATAL, "AAS not initialized\n"); return qfalse; } //load the bot character bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill); if (!bs->character) { BotAI_Print(PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile); return qfalse; } //copy the settings memcpy(&bs->settings, settings, sizeof(bot_settings_t)); //allocate a goal state bs->gs = trap_BotAllocGoalState(client); //load the item weights trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); errnum = trap_BotLoadItemWeights(bs->gs, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); return qfalse; } //allocate a weapon state bs->ws = trap_BotAllocWeaponState(); //load the weapon weights trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); errnum = trap_BotLoadWeaponWeights(bs->ws, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); trap_BotFreeWeaponState(bs->ws); return qfalse; } //allocate a chat state bs->cs = trap_BotAllocChatState(); //load the chat file trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); errnum = trap_BotLoadChatFile(bs->cs, filename, name); if (errnum != BLERR_NOERROR) { trap_BotFreeChatState(bs->cs); trap_BotFreeGoalState(bs->gs); trap_BotFreeWeaponState(bs->ws); return qfalse; } //get the gender characteristic trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); //set the chat gender if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); bs->inuse = qtrue; bs->client = client; bs->entitynum = client; bs->setupcount = 4; bs->entergame_time = FloatTime(); bs->ms = trap_BotAllocMoveState(); bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1); numbots++; if (trap_Cvar_VariableIntegerValue("bot_testichat")) { trap_BotLibVarSet("bot_testichat", "1"); BotChatTest(bs); } //NOTE: reschedule the bot thinking BotScheduleBotThink(); //if interbreeding start with a mutation if (bot_interbreed) { trap_BotMutateGoalFuzzyLogic(bs->gs, 1); } // if we kept the bot client if (restart) { BotReadSessionData(bs); } //bot has been setup succesfully return qtrue; } /* ============== BotAIShutdownClient ============== */ int BotAIShutdownClient(int client, qboolean restart) { bot_state_t *bs; bs = botstates[client]; if (!bs || !bs->inuse) { //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client); return qfalse; } if (restart) { BotWriteSessionData(bs); } if (BotChat_ExitGame(bs)) { trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL); } trap_BotFreeMoveState(bs->ms); //free the goal state` trap_BotFreeGoalState(bs->gs); //free the chat file trap_BotFreeChatState(bs->cs); //free the weapon weights trap_BotFreeWeaponState(bs->ws); //free the bot character trap_BotFreeCharacter(bs->character); // BotFreeWaypoints(bs->checkpoints); BotFreeWaypoints(bs->patrolpoints); //clear activate goal stack BotClearActivateGoalStack(bs); //clear the bot state memset(bs, 0, sizeof(bot_state_t)); //set the inuse flag to qfalse bs->inuse = qfalse; //there's one bot less numbots--; //everything went ok return qtrue; } /* ============== BotResetState called when a bot enters the intermission or observer mode and when the level is changed ============== */ void BotResetState(bot_state_t *bs) { int client, entitynum, inuse; int movestate, goalstate, chatstate, weaponstate; bot_settings_t settings; int character; playerState_t ps; //current player state float entergame_time; //save some things that should not be reset here memcpy(&settings, &bs->settings, sizeof(bot_settings_t)); memcpy(&ps, &bs->cur_ps, sizeof(playerState_t)); inuse = bs->inuse; client = bs->client; entitynum = bs->entitynum; character = bs->character; movestate = bs->ms; goalstate = bs->gs; chatstate = bs->cs; weaponstate = bs->ws; entergame_time = bs->entergame_time; //free checkpoints and patrol points BotFreeWaypoints(bs->checkpoints); BotFreeWaypoints(bs->patrolpoints); //reset the whole state memset(bs, 0, sizeof(bot_state_t)); //copy back some state stuff that should not be reset bs->ms = movestate; bs->gs = goalstate; bs->cs = chatstate; bs->ws = weaponstate; memcpy(&bs->cur_ps, &ps, sizeof(playerState_t)); memcpy(&bs->settings, &settings, sizeof(bot_settings_t)); bs->inuse = inuse; bs->client = client; bs->entitynum = entitynum; bs->character = character; bs->entergame_time = entergame_time; //reset several states if (bs->ms) trap_BotResetMoveState(bs->ms); if (bs->gs) trap_BotResetGoalState(bs->gs); if (bs->ws) trap_BotResetWeaponState(bs->ws); if (bs->gs) trap_BotResetAvoidGoals(bs->gs); if (bs->ms) trap_BotResetAvoidReach(bs->ms); } /* ============== BotAILoadMap ============== */ int BotAILoadMap( int restart ) { int i; vmCvar_t mapname; if (!restart) { trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); trap_BotLibLoadMap( mapname.string ); } for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotResetState( botstates[i] ); botstates[i]->setupcount = 4; } } BotSetupDeathmatchAI(); return qtrue; } void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ); /* ================== BotAIStartFrame ================== */ int BotAIStartFrame(int time) { int i; gentity_t *ent; bot_entitystate_t state; int elapsed_time, thinktime; static int local_time; static int botlib_residual; static int lastbotthink_time; G_CheckBotSpawn(); trap_Cvar_Update(&bot_rocketjump); trap_Cvar_Update(&bot_grapple); trap_Cvar_Update(&bot_fastchat); trap_Cvar_Update(&bot_nochat); trap_Cvar_Update(&bot_testrchat); trap_Cvar_Update(&bot_thinktime); trap_Cvar_Update(&bot_memorydump); trap_Cvar_Update(&bot_saveroutingcache); trap_Cvar_Update(&bot_pause); trap_Cvar_Update(&bot_report); if (bot_report.integer) { // BotTeamplayReport(); // trap_Cvar_Set("bot_report", "0"); BotUpdateInfoConfigStrings(); } if (bot_pause.integer) { // execute bot user commands every frame for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } if( g_entities[i].client->pers.connected != CON_CONNECTED ) { continue; } botstates[i]->lastucmd.forwardmove = 0; botstates[i]->lastucmd.rightmove = 0; botstates[i]->lastucmd.upmove = 0; botstates[i]->lastucmd.buttons = 0; botstates[i]->lastucmd.serverTime = time; trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); } return qtrue; } if (bot_memorydump.integer) { trap_BotLibVarSet("memorydump", "1"); trap_Cvar_Set("bot_memorydump", "0"); } if (bot_saveroutingcache.integer) { trap_BotLibVarSet("saveroutingcache", "1"); trap_Cvar_Set("bot_saveroutingcache", "0"); } //check if bot interbreeding is activated BotInterbreeding(); //cap the bot think time if (bot_thinktime.integer > 200) { trap_Cvar_Set("bot_thinktime", "200"); } //if the bot think time changed we should reschedule the bots if (bot_thinktime.integer != lastbotthink_time) { lastbotthink_time = bot_thinktime.integer; BotScheduleBotThink(); } elapsed_time = time - local_time; local_time = time; botlib_residual += elapsed_time; if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time; else thinktime = bot_thinktime.integer; // update the bot library if ( botlib_residual >= thinktime ) { botlib_residual -= thinktime; trap_BotLibStartFrame((float) time / 1000); if (!trap_AAS_Initialized()) return qfalse; //update entities in the botlib for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; if (!ent->inuse) { trap_BotLibUpdateEntity(i, NULL); continue; } if (!ent->r.linked) { trap_BotLibUpdateEntity(i, NULL); continue; } if ( !(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_LMS ||g_instantgib.integer || g_rockets.integer || g_elimination_allgametypes.integer || g_gametype.integer==GT_CTF_ELIMINATION) && ent->r.svFlags & SVF_NOCLIENT) { trap_BotLibUpdateEntity(i, NULL); continue; } // do not update missiles if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) { trap_BotLibUpdateEntity(i, NULL); continue; } // do not update event only entities if (ent->s.eType > ET_EVENTS) { trap_BotLibUpdateEntity(i, NULL); continue; } // never link prox mine triggers if (ent->r.contents == CONTENTS_TRIGGER) { if (ent->touch == ProximityMine_Trigger) { trap_BotLibUpdateEntity(i, NULL); continue; } } // memset(&state, 0, sizeof(bot_entitystate_t)); // VectorCopy(ent->r.currentOrigin, state.origin); if (i < MAX_CLIENTS) { VectorCopy(ent->s.apos.trBase, state.angles); } else { VectorCopy(ent->r.currentAngles, state.angles); } VectorCopy(ent->s.origin2, state.old_origin); VectorCopy(ent->r.mins, state.mins); VectorCopy(ent->r.maxs, state.maxs); state.type = ent->s.eType; state.flags = ent->s.eFlags; if (ent->r.bmodel) state.solid = SOLID_BSP; else state.solid = SOLID_BBOX; state.groundent = ent->s.groundEntityNum; state.modelindex = ent->s.modelindex; state.modelindex2 = ent->s.modelindex2; state.frame = ent->s.frame; state.event = ent->s.event; state.eventParm = ent->s.eventParm; state.powerups = ent->s.powerups; state.legsAnim = ent->s.legsAnim; state.torsoAnim = ent->s.torsoAnim; state.weapon = ent->s.weapon; // trap_BotLibUpdateEntity(i, &state); } BotAIRegularUpdate(); } floattime = trap_AAS_Time(); // execute scheduled bot AI for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } // botstates[i]->botthink_residual += elapsed_time; // if ( botstates[i]->botthink_residual >= thinktime ) { botstates[i]->botthink_residual -= thinktime; if (!trap_AAS_Initialized()) return qfalse; if (g_entities[i].client->pers.connected == CON_CONNECTED) { BotAI(i, (float) thinktime / 1000); } } } // execute bot user commands every frame for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } if( g_entities[i].client->pers.connected != CON_CONNECTED ) { continue; } BotUpdateInput(botstates[i], time, elapsed_time); trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); } return qtrue; } /* ============== BotInitLibrary ============== */ int BotInitLibrary(void) { char buf[144]; //set the maxclients and maxentities library variables before calling BotSetupLibrary trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "8"); trap_BotLibVarSet("maxclients", buf); Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); trap_BotLibVarSet("maxentities", buf); //bsp checksum trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf); //maximum number of aas links trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf); //maximum number of items in a level trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf); //game type trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "0"); trap_BotLibVarSet("g_gametype", buf); //bot developer mode and log file trap_BotLibVarSet("bot_developer", bot_developer.string); trap_Cvar_VariableStringBuffer("logfile", buf, sizeof(buf)); trap_BotLibVarSet("log", buf); //no chatting trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("nochat", buf); //visualize jump pads trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf); //forced clustering calculations trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf); //forced reachability calculations trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf); //force writing of AAS to file trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf); //no AAS optimization trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf); // trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf); //reload instead of cache bot character files trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "0"); trap_BotLibVarSet("bot_reloadcharacters", buf); //base directory trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("basedir", buf); //game directory trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("gamedir", buf); //home directory trap_Cvar_VariableStringBuffer("fs_homepath", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("homedir", buf); // #ifdef MISSIONPACK trap_BotLibDefine("MISSIONPACK"); #endif //setup the bot library return trap_BotLibSetup(); } /* ============== BotAISetup ============== */ int BotAISetup( int restart ) { int errnum; trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT); trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_developer, "bot_developer", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0); trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0); trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0); trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0); //if the game is restarted for a tournament if (restart) { return qtrue; } //initialize the bot states memset( botstates, 0, sizeof(botstates) ); errnum = BotInitLibrary(); if (errnum != BLERR_NOERROR) return qfalse; return qtrue; } /* ============== BotAIShutdown ============== */ int BotAIShutdown( int restart ) { int i; //if the game is restarted for a tournament if ( restart ) { //shutdown all the bots in the botlib for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotAIShutdownClient(botstates[i]->client, restart); } } //don't shutdown the bot library } else { trap_BotLibShutdown(); } return qtrue; } openarena_0.8.8.orig/code/game/g_syscalls.c0000644000175000017500000006122011656310264017361 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" // this file is only included when building a dll // g_syscalls.asm is included instead when building a qvm #ifdef Q3_VM #error "Do not use in VM build" #endif static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1; Q_EXPORT void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) { syscall = syscallptr; } int PASSFLOAT( float x ) { floatint_t fi; fi.f = x; return fi.i; } void trap_Printf( const char *fmt ) { syscall( G_PRINT, fmt ); } void trap_Error( const char *fmt ) { syscall( G_ERROR, fmt ); exit(0); //Will never be executed. Makes compiler happy } int trap_Milliseconds( void ) { return syscall( G_MILLISECONDS ); } int trap_Argc( void ) { return syscall( G_ARGC ); } void trap_Argv( int n, char *buffer, int bufferLength ) { syscall( G_ARGV, n, buffer, bufferLength ); } int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { return syscall( G_FS_FOPEN_FILE, qpath, f, mode ); } void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { syscall( G_FS_READ, buffer, len, f ); } void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { syscall( G_FS_WRITE, buffer, len, f ); } void trap_FS_FCloseFile( fileHandle_t f ) { syscall( G_FS_FCLOSE_FILE, f ); } int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ) { return syscall( G_FS_GETFILELIST, path, extension, listbuf, bufsize ); } int trap_FS_Seek( fileHandle_t f, long offset, int origin ) { return syscall( G_FS_SEEK, f, offset, origin ); } void trap_SendConsoleCommand( int exec_when, const char *text ) { syscall( G_SEND_CONSOLE_COMMAND, exec_when, text ); } void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags ) { syscall( G_CVAR_REGISTER, cvar, var_name, value, flags ); } void trap_Cvar_Update( vmCvar_t *cvar ) { syscall( G_CVAR_UPDATE, cvar ); } void trap_Cvar_Set( const char *var_name, const char *value ) { syscall( G_CVAR_SET, var_name, value ); } int trap_Cvar_VariableIntegerValue( const char *var_name ) { return syscall( G_CVAR_VARIABLE_INTEGER_VALUE, var_name ); } void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { syscall( G_CVAR_VARIABLE_STRING_BUFFER, var_name, buffer, bufsize ); } void trap_LocateGameData( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t, playerState_t *clients, int sizeofGClient ) { syscall( G_LOCATE_GAME_DATA, gEnts, numGEntities, sizeofGEntity_t, clients, sizeofGClient ); } void trap_DropClient( int clientNum, const char *reason ) { syscall( G_DROP_CLIENT, clientNum, reason ); } void trap_SendServerCommand( int clientNum, const char *text ) { syscall( G_SEND_SERVER_COMMAND, clientNum, text ); } void trap_SetConfigstring( int num, const char *string ) { syscall( G_SET_CONFIGSTRING, num, string ); } void trap_GetConfigstring( int num, char *buffer, int bufferSize ) { syscall( G_GET_CONFIGSTRING, num, buffer, bufferSize ); } void trap_GetUserinfo( int num, char *buffer, int bufferSize ) { syscall( G_GET_USERINFO, num, buffer, bufferSize ); } void trap_SetUserinfo( int num, const char *buffer ) { syscall( G_SET_USERINFO, num, buffer ); } void trap_GetServerinfo( char *buffer, int bufferSize ) { syscall( G_GET_SERVERINFO, buffer, bufferSize ); } void trap_SetBrushModel( gentity_t *ent, const char *name ) { syscall( G_SET_BRUSH_MODEL, ent, name ); } void trap_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask ) { syscall( G_TRACE, results, start, mins, maxs, end, passEntityNum, contentmask ); } void trap_TraceCapsule( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask ) { syscall( G_TRACECAPSULE, results, start, mins, maxs, end, passEntityNum, contentmask ); } int trap_PointContents( const vec3_t point, int passEntityNum ) { return syscall( G_POINT_CONTENTS, point, passEntityNum ); } qboolean trap_InPVS( const vec3_t p1, const vec3_t p2 ) { return syscall( G_IN_PVS, p1, p2 ); } qboolean trap_InPVSIgnorePortals( const vec3_t p1, const vec3_t p2 ) { return syscall( G_IN_PVS_IGNORE_PORTALS, p1, p2 ); } void trap_AdjustAreaPortalState( gentity_t *ent, qboolean open ) { syscall( G_ADJUST_AREA_PORTAL_STATE, ent, open ); } qboolean trap_AreasConnected( int area1, int area2 ) { return syscall( G_AREAS_CONNECTED, area1, area2 ); } void trap_LinkEntity( gentity_t *ent ) { syscall( G_LINKENTITY, ent ); } void trap_UnlinkEntity( gentity_t *ent ) { syscall( G_UNLINKENTITY, ent ); } int trap_EntitiesInBox( const vec3_t mins, const vec3_t maxs, int *list, int maxcount ) { return syscall( G_ENTITIES_IN_BOX, mins, maxs, list, maxcount ); } qboolean trap_EntityContact( const vec3_t mins, const vec3_t maxs, const gentity_t *ent ) { return syscall( G_ENTITY_CONTACT, mins, maxs, ent ); } qboolean trap_EntityContactCapsule( const vec3_t mins, const vec3_t maxs, const gentity_t *ent ) { return syscall( G_ENTITY_CONTACTCAPSULE, mins, maxs, ent ); } int trap_BotAllocateClient( void ) { return syscall( G_BOT_ALLOCATE_CLIENT ); } void trap_BotFreeClient( int clientNum ) { syscall( G_BOT_FREE_CLIENT, clientNum ); } void trap_GetUsercmd( int clientNum, usercmd_t *cmd ) { syscall( G_GET_USERCMD, clientNum, cmd ); } qboolean trap_GetEntityToken( char *buffer, int bufferSize ) { return syscall( G_GET_ENTITY_TOKEN, buffer, bufferSize ); } int trap_DebugPolygonCreate(int color, int numPoints, vec3_t *points) { return syscall( G_DEBUG_POLYGON_CREATE, color, numPoints, points ); } void trap_DebugPolygonDelete(int id) { syscall( G_DEBUG_POLYGON_DELETE, id ); } int trap_RealTime( qtime_t *qtime ) { return syscall( G_REAL_TIME, qtime ); } void trap_SnapVector( float *v ) { syscall( G_SNAPVECTOR, v ); return; } // BotLib traps start here int trap_BotLibSetup( void ) { return syscall( BOTLIB_SETUP ); } int trap_BotLibShutdown( void ) { return syscall( BOTLIB_SHUTDOWN ); } int trap_BotLibVarSet(char *var_name, char *value) { return syscall( BOTLIB_LIBVAR_SET, var_name, value ); } int trap_BotLibVarGet(char *var_name, char *value, int size) { return syscall( BOTLIB_LIBVAR_GET, var_name, value, size ); } int trap_BotLibDefine(char *string) { return syscall( BOTLIB_PC_ADD_GLOBAL_DEFINE, string ); } int trap_BotLibStartFrame(float time) { return syscall( BOTLIB_START_FRAME, PASSFLOAT( time ) ); } int trap_BotLibLoadMap(const char *mapname) { return syscall( BOTLIB_LOAD_MAP, mapname ); } int trap_BotLibUpdateEntity(int ent, void /* struct bot_updateentity_s */ *bue) { return syscall( BOTLIB_UPDATENTITY, ent, bue ); } int trap_BotLibTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) { return syscall( BOTLIB_TEST, parm0, parm1, parm2, parm3 ); } int trap_BotGetSnapshotEntity( int clientNum, int sequence ) { return syscall( BOTLIB_GET_SNAPSHOT_ENTITY, clientNum, sequence ); } int trap_BotGetServerCommand(int clientNum, char *message, int size) { return syscall( BOTLIB_GET_CONSOLE_MESSAGE, clientNum, message, size ); } void trap_BotUserCommand(int clientNum, usercmd_t *ucmd) { syscall( BOTLIB_USER_COMMAND, clientNum, ucmd ); } void trap_AAS_EntityInfo(int entnum, void /* struct aas_entityinfo_s */ *info) { syscall( BOTLIB_AAS_ENTITY_INFO, entnum, info ); } int trap_AAS_Initialized(void) { return syscall( BOTLIB_AAS_INITIALIZED ); } void trap_AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs) { syscall( BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX, presencetype, mins, maxs ); } float trap_AAS_Time(void) { floatint_t fi; fi.i = syscall( BOTLIB_AAS_TIME ); return fi.f; } int trap_AAS_PointAreaNum(vec3_t point) { return syscall( BOTLIB_AAS_POINT_AREA_NUM, point ); } int trap_AAS_PointReachabilityAreaIndex(vec3_t point) { return syscall( BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX, point ); } int trap_AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) { return syscall( BOTLIB_AAS_TRACE_AREAS, start, end, areas, points, maxareas ); } int trap_AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) { return syscall( BOTLIB_AAS_BBOX_AREAS, absmins, absmaxs, areas, maxareas ); } int trap_AAS_AreaInfo( int areanum, void /* struct aas_areainfo_s */ *info ) { return syscall( BOTLIB_AAS_AREA_INFO, areanum, info ); } int trap_AAS_PointContents(vec3_t point) { return syscall( BOTLIB_AAS_POINT_CONTENTS, point ); } int trap_AAS_NextBSPEntity(int ent) { return syscall( BOTLIB_AAS_NEXT_BSP_ENTITY, ent ); } int trap_AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size) { return syscall( BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY, ent, key, value, size ); } int trap_AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v) { return syscall( BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY, ent, key, v ); } int trap_AAS_FloatForBSPEpairKey(int ent, char *key, float *value) { return syscall( BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY, ent, key, value ); } int trap_AAS_IntForBSPEpairKey(int ent, char *key, int *value) { return syscall( BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY, ent, key, value ); } int trap_AAS_AreaReachability(int areanum) { return syscall( BOTLIB_AAS_AREA_REACHABILITY, areanum ); } int trap_AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) { return syscall( BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA, areanum, origin, goalareanum, travelflags ); } int trap_AAS_EnableRoutingArea( int areanum, int enable ) { return syscall( BOTLIB_AAS_ENABLE_ROUTING_AREA, areanum, enable ); } int trap_AAS_PredictRoute(void /*struct aas_predictroute_s*/ *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum) { return syscall( BOTLIB_AAS_PREDICT_ROUTE, route, areanum, origin, goalareanum, travelflags, maxareas, maxtime, stopevent, stopcontents, stoptfl, stopareanum ); } int trap_AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, void /*struct aas_altroutegoal_s*/ *altroutegoals, int maxaltroutegoals, int type) { return syscall( BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL, start, startareanum, goal, goalareanum, travelflags, altroutegoals, maxaltroutegoals, type ); } int trap_AAS_Swimming(vec3_t origin) { return syscall( BOTLIB_AAS_SWIMMING, origin ); } int trap_AAS_PredictClientMovement(void /* struct aas_clientmove_s */ *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize) { return syscall( BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT, move, entnum, origin, presencetype, onground, velocity, cmdmove, cmdframes, maxframes, PASSFLOAT(frametime), stopevent, stopareanum, visualize ); } void trap_EA_Say(int client, char *str) { syscall( BOTLIB_EA_SAY, client, str ); } void trap_EA_SayTeam(int client, char *str) { syscall( BOTLIB_EA_SAY_TEAM, client, str ); } void trap_EA_Command(int client, char *command) { syscall( BOTLIB_EA_COMMAND, client, command ); } void trap_EA_Action(int client, int action) { syscall( BOTLIB_EA_ACTION, client, action ); } void trap_EA_Gesture(int client) { syscall( BOTLIB_EA_GESTURE, client ); } void trap_EA_Talk(int client) { syscall( BOTLIB_EA_TALK, client ); } void trap_EA_Attack(int client) { syscall( BOTLIB_EA_ATTACK, client ); } void trap_EA_Use(int client) { syscall( BOTLIB_EA_USE, client ); } void trap_EA_Respawn(int client) { syscall( BOTLIB_EA_RESPAWN, client ); } void trap_EA_Crouch(int client) { syscall( BOTLIB_EA_CROUCH, client ); } void trap_EA_MoveUp(int client) { syscall( BOTLIB_EA_MOVE_UP, client ); } void trap_EA_MoveDown(int client) { syscall( BOTLIB_EA_MOVE_DOWN, client ); } void trap_EA_MoveForward(int client) { syscall( BOTLIB_EA_MOVE_FORWARD, client ); } void trap_EA_MoveBack(int client) { syscall( BOTLIB_EA_MOVE_BACK, client ); } void trap_EA_MoveLeft(int client) { syscall( BOTLIB_EA_MOVE_LEFT, client ); } void trap_EA_MoveRight(int client) { syscall( BOTLIB_EA_MOVE_RIGHT, client ); } void trap_EA_SelectWeapon(int client, int weapon) { syscall( BOTLIB_EA_SELECT_WEAPON, client, weapon ); } void trap_EA_Jump(int client) { syscall( BOTLIB_EA_JUMP, client ); } void trap_EA_DelayedJump(int client) { syscall( BOTLIB_EA_DELAYED_JUMP, client ); } void trap_EA_Move(int client, vec3_t dir, float speed) { syscall( BOTLIB_EA_MOVE, client, dir, PASSFLOAT(speed) ); } void trap_EA_View(int client, vec3_t viewangles) { syscall( BOTLIB_EA_VIEW, client, viewangles ); } void trap_EA_EndRegular(int client, float thinktime) { syscall( BOTLIB_EA_END_REGULAR, client, PASSFLOAT(thinktime) ); } void trap_EA_GetInput(int client, float thinktime, void /* struct bot_input_s */ *input) { syscall( BOTLIB_EA_GET_INPUT, client, PASSFLOAT(thinktime), input ); } void trap_EA_ResetInput(int client) { syscall( BOTLIB_EA_RESET_INPUT, client ); } int trap_BotLoadCharacter(char *charfile, float skill) { return syscall( BOTLIB_AI_LOAD_CHARACTER, charfile, PASSFLOAT(skill)); } void trap_BotFreeCharacter(int character) { syscall( BOTLIB_AI_FREE_CHARACTER, character ); } float trap_Characteristic_Float(int character, int index) { floatint_t fi; fi.i = syscall( BOTLIB_AI_CHARACTERISTIC_FLOAT, character, index ); return fi.f; } float trap_Characteristic_BFloat(int character, int index, float min, float max) { floatint_t fi; fi.i = syscall( BOTLIB_AI_CHARACTERISTIC_BFLOAT, character, index, PASSFLOAT(min), PASSFLOAT(max) ); return fi.f; } int trap_Characteristic_Integer(int character, int index) { return syscall( BOTLIB_AI_CHARACTERISTIC_INTEGER, character, index ); } int trap_Characteristic_BInteger(int character, int index, int min, int max) { return syscall( BOTLIB_AI_CHARACTERISTIC_BINTEGER, character, index, min, max ); } void trap_Characteristic_String(int character, int index, char *buf, int size) { syscall( BOTLIB_AI_CHARACTERISTIC_STRING, character, index, buf, size ); } int trap_BotAllocChatState(void) { return syscall( BOTLIB_AI_ALLOC_CHAT_STATE ); } void trap_BotFreeChatState(int handle) { syscall( BOTLIB_AI_FREE_CHAT_STATE, handle ); } void trap_BotQueueConsoleMessage(int chatstate, int type, char *message) { syscall( BOTLIB_AI_QUEUE_CONSOLE_MESSAGE, chatstate, type, message ); } void trap_BotRemoveConsoleMessage(int chatstate, int handle) { syscall( BOTLIB_AI_REMOVE_CONSOLE_MESSAGE, chatstate, handle ); } int trap_BotNextConsoleMessage(int chatstate, void /* struct bot_consolemessage_s */ *cm) { return syscall( BOTLIB_AI_NEXT_CONSOLE_MESSAGE, chatstate, cm ); } int trap_BotNumConsoleMessages(int chatstate) { return syscall( BOTLIB_AI_NUM_CONSOLE_MESSAGE, chatstate ); } void trap_BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7 ) { syscall( BOTLIB_AI_INITIAL_CHAT, chatstate, type, mcontext, var0, var1, var2, var3, var4, var5, var6, var7 ); } int trap_BotNumInitialChats(int chatstate, char *type) { return syscall( BOTLIB_AI_NUM_INITIAL_CHATS, chatstate, type ); } int trap_BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7 ) { return syscall( BOTLIB_AI_REPLY_CHAT, chatstate, message, mcontext, vcontext, var0, var1, var2, var3, var4, var5, var6, var7 ); } int trap_BotChatLength(int chatstate) { return syscall( BOTLIB_AI_CHAT_LENGTH, chatstate ); } void trap_BotEnterChat(int chatstate, int client, int sendto) { syscall( BOTLIB_AI_ENTER_CHAT, chatstate, client, sendto ); } void trap_BotGetChatMessage(int chatstate, char *buf, int size) { syscall( BOTLIB_AI_GET_CHAT_MESSAGE, chatstate, buf, size); } int trap_StringContains(char *str1, char *str2, int casesensitive) { return syscall( BOTLIB_AI_STRING_CONTAINS, str1, str2, casesensitive ); } int trap_BotFindMatch(char *str, void /* struct bot_match_s */ *match, unsigned long int context) { return syscall( BOTLIB_AI_FIND_MATCH, str, match, context ); } void trap_BotMatchVariable(void /* struct bot_match_s */ *match, int variable, char *buf, int size) { syscall( BOTLIB_AI_MATCH_VARIABLE, match, variable, buf, size ); } void trap_UnifyWhiteSpaces(char *string) { syscall( BOTLIB_AI_UNIFY_WHITE_SPACES, string ); } void trap_BotReplaceSynonyms(char *string, unsigned long int context) { syscall( BOTLIB_AI_REPLACE_SYNONYMS, string, context ); } int trap_BotLoadChatFile(int chatstate, char *chatfile, char *chatname) { return syscall( BOTLIB_AI_LOAD_CHAT_FILE, chatstate, chatfile, chatname ); } void trap_BotSetChatGender(int chatstate, int gender) { syscall( BOTLIB_AI_SET_CHAT_GENDER, chatstate, gender ); } void trap_BotSetChatName(int chatstate, char *name, int client) { syscall( BOTLIB_AI_SET_CHAT_NAME, chatstate, name, client ); } void trap_BotResetGoalState(int goalstate) { syscall( BOTLIB_AI_RESET_GOAL_STATE, goalstate ); } void trap_BotResetAvoidGoals(int goalstate) { syscall( BOTLIB_AI_RESET_AVOID_GOALS, goalstate ); } void trap_BotRemoveFromAvoidGoals(int goalstate, int number) { syscall( BOTLIB_AI_REMOVE_FROM_AVOID_GOALS, goalstate, number); } void trap_BotPushGoal(int goalstate, void /* struct bot_goal_s */ *goal) { syscall( BOTLIB_AI_PUSH_GOAL, goalstate, goal ); } void trap_BotPopGoal(int goalstate) { syscall( BOTLIB_AI_POP_GOAL, goalstate ); } void trap_BotEmptyGoalStack(int goalstate) { syscall( BOTLIB_AI_EMPTY_GOAL_STACK, goalstate ); } void trap_BotDumpAvoidGoals(int goalstate) { syscall( BOTLIB_AI_DUMP_AVOID_GOALS, goalstate ); } void trap_BotDumpGoalStack(int goalstate) { syscall( BOTLIB_AI_DUMP_GOAL_STACK, goalstate ); } void trap_BotGoalName(int number, char *name, int size) { syscall( BOTLIB_AI_GOAL_NAME, number, name, size ); } int trap_BotGetTopGoal(int goalstate, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_GET_TOP_GOAL, goalstate, goal ); } int trap_BotGetSecondGoal(int goalstate, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_GET_SECOND_GOAL, goalstate, goal ); } int trap_BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags) { return syscall( BOTLIB_AI_CHOOSE_LTG_ITEM, goalstate, origin, inventory, travelflags ); } int trap_BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, void /* struct bot_goal_s */ *ltg, float maxtime) { return syscall( BOTLIB_AI_CHOOSE_NBG_ITEM, goalstate, origin, inventory, travelflags, ltg, PASSFLOAT(maxtime) ); } int trap_BotTouchingGoal(vec3_t origin, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_TOUCHING_GOAL, origin, goal ); } int trap_BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE, viewer, eye, viewangles, goal ); } int trap_BotGetLevelItemGoal(int index, char *classname, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_GET_LEVEL_ITEM_GOAL, index, classname, goal ); } int trap_BotGetNextCampSpotGoal(int num, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL, num, goal ); } int trap_BotGetMapLocationGoal(char *name, void /* struct bot_goal_s */ *goal) { return syscall( BOTLIB_AI_GET_MAP_LOCATION_GOAL, name, goal ); } float trap_BotAvoidGoalTime(int goalstate, int number) { floatint_t fi; fi.i = syscall( BOTLIB_AI_AVOID_GOAL_TIME, goalstate, number ); return fi.f; } void trap_BotSetAvoidGoalTime(int goalstate, int number, float avoidtime) { syscall( BOTLIB_AI_SET_AVOID_GOAL_TIME, goalstate, number, PASSFLOAT(avoidtime)); } void trap_BotInitLevelItems(void) { syscall( BOTLIB_AI_INIT_LEVEL_ITEMS ); } void trap_BotUpdateEntityItems(void) { syscall( BOTLIB_AI_UPDATE_ENTITY_ITEMS ); } int trap_BotLoadItemWeights(int goalstate, char *filename) { return syscall( BOTLIB_AI_LOAD_ITEM_WEIGHTS, goalstate, filename ); } void trap_BotFreeItemWeights(int goalstate) { syscall( BOTLIB_AI_FREE_ITEM_WEIGHTS, goalstate ); } void trap_BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child) { syscall( BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC, parent1, parent2, child ); } void trap_BotSaveGoalFuzzyLogic(int goalstate, char *filename) { syscall( BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC, goalstate, filename ); } void trap_BotMutateGoalFuzzyLogic(int goalstate, float range) { syscall( BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC, goalstate, range ); } int trap_BotAllocGoalState(int state) { return syscall( BOTLIB_AI_ALLOC_GOAL_STATE, state ); } void trap_BotFreeGoalState(int handle) { syscall( BOTLIB_AI_FREE_GOAL_STATE, handle ); } void trap_BotResetMoveState(int movestate) { syscall( BOTLIB_AI_RESET_MOVE_STATE, movestate ); } void trap_BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type) { syscall( BOTLIB_AI_ADD_AVOID_SPOT, movestate, origin, PASSFLOAT(radius), type); } void trap_BotMoveToGoal(void /* struct bot_moveresult_s */ *result, int movestate, void /* struct bot_goal_s */ *goal, int travelflags) { syscall( BOTLIB_AI_MOVE_TO_GOAL, result, movestate, goal, travelflags ); } int trap_BotMoveInDirection(int movestate, vec3_t dir, float speed, int type) { return syscall( BOTLIB_AI_MOVE_IN_DIRECTION, movestate, dir, PASSFLOAT(speed), type ); } void trap_BotResetAvoidReach(int movestate) { syscall( BOTLIB_AI_RESET_AVOID_REACH, movestate ); } void trap_BotResetLastAvoidReach(int movestate) { syscall( BOTLIB_AI_RESET_LAST_AVOID_REACH,movestate ); } int trap_BotReachabilityArea(vec3_t origin, int testground) { return syscall( BOTLIB_AI_REACHABILITY_AREA, origin, testground ); } int trap_BotMovementViewTarget(int movestate, void /* struct bot_goal_s */ *goal, int travelflags, float lookahead, vec3_t target) { return syscall( BOTLIB_AI_MOVEMENT_VIEW_TARGET, movestate, goal, travelflags, PASSFLOAT(lookahead), target ); } int trap_BotPredictVisiblePosition(vec3_t origin, int areanum, void /* struct bot_goal_s */ *goal, int travelflags, vec3_t target) { return syscall( BOTLIB_AI_PREDICT_VISIBLE_POSITION, origin, areanum, goal, travelflags, target ); } int trap_BotAllocMoveState(void) { return syscall( BOTLIB_AI_ALLOC_MOVE_STATE ); } void trap_BotFreeMoveState(int handle) { syscall( BOTLIB_AI_FREE_MOVE_STATE, handle ); } void trap_BotInitMoveState(int handle, void /* struct bot_initmove_s */ *initmove) { syscall( BOTLIB_AI_INIT_MOVE_STATE, handle, initmove ); } int trap_BotChooseBestFightWeapon(int weaponstate, int *inventory) { return syscall( BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON, weaponstate, inventory ); } void trap_BotGetWeaponInfo(int weaponstate, int weapon, void /* struct weaponinfo_s */ *weaponinfo) { syscall( BOTLIB_AI_GET_WEAPON_INFO, weaponstate, weapon, weaponinfo ); } int trap_BotLoadWeaponWeights(int weaponstate, char *filename) { return syscall( BOTLIB_AI_LOAD_WEAPON_WEIGHTS, weaponstate, filename ); } int trap_BotAllocWeaponState(void) { return syscall( BOTLIB_AI_ALLOC_WEAPON_STATE ); } void trap_BotFreeWeaponState(int weaponstate) { syscall( BOTLIB_AI_FREE_WEAPON_STATE, weaponstate ); } void trap_BotResetWeaponState(int weaponstate) { syscall( BOTLIB_AI_RESET_WEAPON_STATE, weaponstate ); } int trap_GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child) { return syscall( BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION, numranks, ranks, parent1, parent2, child ); } int trap_PC_LoadSource( const char *filename ) { return syscall( BOTLIB_PC_LOAD_SOURCE, filename ); } int trap_PC_FreeSource( int handle ) { return syscall( BOTLIB_PC_FREE_SOURCE, handle ); } int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { return syscall( BOTLIB_PC_READ_TOKEN, handle, pc_token ); } int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { return syscall( BOTLIB_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); } openarena_0.8.8.orig/code/game/bg_lib.c0000644000175000017500000016407211656310264016445 0ustar smcvsmcv// // // bg_lib,c -- standard C library replacement routines used by code // compiled for the virtual machine #ifdef Q3_VM #include "../qcommon/q_shared.h" /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "bg_lib.h" #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = #endif /* LIBC_SCCS and not lint */ static char* med3(char *, char *, char *, cmp_t *); static void swapfunc(char *, char *, int, int); #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif /* * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ register TYPE *pi = (TYPE *) (parmi); \ register TYPE *pj = (TYPE *) (parmj); \ do { \ register TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; static void swapfunc(a, b, n, swaptype) char *a, *b; int n, swaptype; { if(swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) \ if (swaptype == 0) { \ long t = *(long *)(a); \ *(long *)(a) = *(long *)(b); \ *(long *)(b) = t; \ } else \ swapfunc(a, b, es, swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) static char * med3(a, b, c, cmp) char *a, *b, *c; cmp_t *cmp; { return cmp(a, b) < 0 ? (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); } void qsort(a, n, es, cmp) void *a; size_t n, es; cmp_t *cmp; { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; loop: SWAPINIT(a, es); swap_cnt = 0; if (n < 7) { for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pm = (char *)a + (n / 2) * es; if (n > 7) { pl = a; pn = (char *)a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; pl = med3(pl, pl + d, pl + 2 * d, cmp); pm = med3(pm - d, pm, pm + d, cmp); pn = med3(pn - 2 * d, pn - d, pn, cmp); } pm = med3(pl, pm, pn, cmp); } swap(a, pm); pa = pb = (char *)a + es; pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) { if (r == 0) { swap_cnt = 1; swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = cmp(pc, a)) >= 0) { if (r == 0) { swap_cnt = 1; swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); swap_cnt = 1; pb += es; pc -= es; } if (swap_cnt == 0) { /* Switch to insertion sort */ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pn = (char *)a + n * es; r = min(pa - (char *)a, pb - pa); vecswap(a, pb - r, r); r = min(pd - pc, pn - pd - es); vecswap(pb, pn - r, r); if ((r = pb - pa) > es) qsort(a, r / es, es, cmp); if ((r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } /* qsort(pn - r, r / es, es, cmp);*/ } //================================================================================== size_t strlen( const char *string ) { const char *s; s = string; while ( *s ) { s++; } return s - string; } char *strcat( char *strDestination, const char *strSource ) { char *s; s = strDestination; while ( *s ) { s++; } while ( *strSource ) { *s++ = *strSource++; } *s = 0; return strDestination; } char *strcpy( char *strDestination, const char *strSource ) { char *s; s = strDestination; while ( *strSource ) { *s++ = *strSource++; } *s = 0; return strDestination; } int strcmp( const char *string1, const char *string2 ) { while ( *string1 == *string2 && *string1 && *string2 ) { string1++; string2++; } return *string1 - *string2; } char *strchr( const char *string, int c ) { while ( *string ) { if ( *string == c ) { return ( char * )string; } string++; } if(c) return NULL; else return (char*) string; } char *strrchr(const char *string, int c) { const char *found = NULL; while(*string) { if(*string == c) found = string; string++; } if(c) return (char *) found; else return (char *) string; } char *strstr( const char *string, const char *strCharSet ) { while ( *string ) { int i; for ( i = 0 ; strCharSet[i] ; i++ ) { if ( string[i] != strCharSet[i] ) { break; } } if ( !strCharSet[i] ) { return (char *)string; } string++; } return (char *)0; } int tolower( int c ) { if ( c >= 'A' && c <= 'Z' ) { c += 'a' - 'A'; } return c; } int toupper( int c ) { if ( c >= 'a' && c <= 'z' ) { c += 'A' - 'a'; } return c; } void *memmove( void *dest, const void *src, size_t count ) { int i; if ( dest > src ) { for ( i = count-1 ; i >= 0 ; i-- ) { ((char *)dest)[i] = ((char *)src)[i]; } } else { for ( i = 0 ; i < count ; i++ ) { ((char *)dest)[i] = ((char *)src)[i]; } } return dest; } #if 0 double floor( double x ) { return (int)(x + 0x40000000) - 0x40000000; } void *memset( void *dest, int c, size_t count ) { while ( count-- ) { ((char *)dest)[count] = c; } return dest; } void *memcpy( void *dest, const void *src, size_t count ) { while ( count-- ) { ((char *)dest)[count] = ((char *)src)[count]; } return dest; } char *strncpy( char *strDest, const char *strSource, size_t count ) { char *s; s = strDest; while ( *strSource && count ) { *s++ = *strSource++; count--; } while ( count-- ) { *s++ = 0; } return strDest; } double sqrt( double x ) { float y; float delta; float maxError; if ( x <= 0 ) { return 0; } // initial guess y = x / 2; // refine maxError = x * 0.001; do { delta = ( y * y ) - x; y -= delta / ( 2 * y ); } while ( delta > maxError || delta < -maxError ); return y; } float sintable[1024] = { 0.000000,0.001534,0.003068,0.004602,0.006136,0.007670,0.009204,0.010738, 0.012272,0.013805,0.015339,0.016873,0.018407,0.019940,0.021474,0.023008, 0.024541,0.026075,0.027608,0.029142,0.030675,0.032208,0.033741,0.035274, 0.036807,0.038340,0.039873,0.041406,0.042938,0.044471,0.046003,0.047535, 0.049068,0.050600,0.052132,0.053664,0.055195,0.056727,0.058258,0.059790, 0.061321,0.062852,0.064383,0.065913,0.067444,0.068974,0.070505,0.072035, 0.073565,0.075094,0.076624,0.078153,0.079682,0.081211,0.082740,0.084269, 0.085797,0.087326,0.088854,0.090381,0.091909,0.093436,0.094963,0.096490, 0.098017,0.099544,0.101070,0.102596,0.104122,0.105647,0.107172,0.108697, 0.110222,0.111747,0.113271,0.114795,0.116319,0.117842,0.119365,0.120888, 0.122411,0.123933,0.125455,0.126977,0.128498,0.130019,0.131540,0.133061, 0.134581,0.136101,0.137620,0.139139,0.140658,0.142177,0.143695,0.145213, 0.146730,0.148248,0.149765,0.151281,0.152797,0.154313,0.155828,0.157343, 0.158858,0.160372,0.161886,0.163400,0.164913,0.166426,0.167938,0.169450, 0.170962,0.172473,0.173984,0.175494,0.177004,0.178514,0.180023,0.181532, 0.183040,0.184548,0.186055,0.187562,0.189069,0.190575,0.192080,0.193586, 0.195090,0.196595,0.198098,0.199602,0.201105,0.202607,0.204109,0.205610, 0.207111,0.208612,0.210112,0.211611,0.213110,0.214609,0.216107,0.217604, 0.219101,0.220598,0.222094,0.223589,0.225084,0.226578,0.228072,0.229565, 0.231058,0.232550,0.234042,0.235533,0.237024,0.238514,0.240003,0.241492, 0.242980,0.244468,0.245955,0.247442,0.248928,0.250413,0.251898,0.253382, 0.254866,0.256349,0.257831,0.259313,0.260794,0.262275,0.263755,0.265234, 0.266713,0.268191,0.269668,0.271145,0.272621,0.274097,0.275572,0.277046, 0.278520,0.279993,0.281465,0.282937,0.284408,0.285878,0.287347,0.288816, 0.290285,0.291752,0.293219,0.294685,0.296151,0.297616,0.299080,0.300543, 0.302006,0.303468,0.304929,0.306390,0.307850,0.309309,0.310767,0.312225, 0.313682,0.315138,0.316593,0.318048,0.319502,0.320955,0.322408,0.323859, 0.325310,0.326760,0.328210,0.329658,0.331106,0.332553,0.334000,0.335445, 0.336890,0.338334,0.339777,0.341219,0.342661,0.344101,0.345541,0.346980, 0.348419,0.349856,0.351293,0.352729,0.354164,0.355598,0.357031,0.358463, 0.359895,0.361326,0.362756,0.364185,0.365613,0.367040,0.368467,0.369892, 0.371317,0.372741,0.374164,0.375586,0.377007,0.378428,0.379847,0.381266, 0.382683,0.384100,0.385516,0.386931,0.388345,0.389758,0.391170,0.392582, 0.393992,0.395401,0.396810,0.398218,0.399624,0.401030,0.402435,0.403838, 0.405241,0.406643,0.408044,0.409444,0.410843,0.412241,0.413638,0.415034, 0.416430,0.417824,0.419217,0.420609,0.422000,0.423390,0.424780,0.426168, 0.427555,0.428941,0.430326,0.431711,0.433094,0.434476,0.435857,0.437237, 0.438616,0.439994,0.441371,0.442747,0.444122,0.445496,0.446869,0.448241, 0.449611,0.450981,0.452350,0.453717,0.455084,0.456449,0.457813,0.459177, 0.460539,0.461900,0.463260,0.464619,0.465976,0.467333,0.468689,0.470043, 0.471397,0.472749,0.474100,0.475450,0.476799,0.478147,0.479494,0.480839, 0.482184,0.483527,0.484869,0.486210,0.487550,0.488889,0.490226,0.491563, 0.492898,0.494232,0.495565,0.496897,0.498228,0.499557,0.500885,0.502212, 0.503538,0.504863,0.506187,0.507509,0.508830,0.510150,0.511469,0.512786, 0.514103,0.515418,0.516732,0.518045,0.519356,0.520666,0.521975,0.523283, 0.524590,0.525895,0.527199,0.528502,0.529804,0.531104,0.532403,0.533701, 0.534998,0.536293,0.537587,0.538880,0.540171,0.541462,0.542751,0.544039, 0.545325,0.546610,0.547894,0.549177,0.550458,0.551738,0.553017,0.554294, 0.555570,0.556845,0.558119,0.559391,0.560662,0.561931,0.563199,0.564466, 0.565732,0.566996,0.568259,0.569521,0.570781,0.572040,0.573297,0.574553, 0.575808,0.577062,0.578314,0.579565,0.580814,0.582062,0.583309,0.584554, 0.585798,0.587040,0.588282,0.589521,0.590760,0.591997,0.593232,0.594466, 0.595699,0.596931,0.598161,0.599389,0.600616,0.601842,0.603067,0.604290, 0.605511,0.606731,0.607950,0.609167,0.610383,0.611597,0.612810,0.614022, 0.615232,0.616440,0.617647,0.618853,0.620057,0.621260,0.622461,0.623661, 0.624859,0.626056,0.627252,0.628446,0.629638,0.630829,0.632019,0.633207, 0.634393,0.635578,0.636762,0.637944,0.639124,0.640303,0.641481,0.642657, 0.643832,0.645005,0.646176,0.647346,0.648514,0.649681,0.650847,0.652011, 0.653173,0.654334,0.655493,0.656651,0.657807,0.658961,0.660114,0.661266, 0.662416,0.663564,0.664711,0.665856,0.667000,0.668142,0.669283,0.670422, 0.671559,0.672695,0.673829,0.674962,0.676093,0.677222,0.678350,0.679476, 0.680601,0.681724,0.682846,0.683965,0.685084,0.686200,0.687315,0.688429, 0.689541,0.690651,0.691759,0.692866,0.693971,0.695075,0.696177,0.697278, 0.698376,0.699473,0.700569,0.701663,0.702755,0.703845,0.704934,0.706021, 0.707107,0.708191,0.709273,0.710353,0.711432,0.712509,0.713585,0.714659, 0.715731,0.716801,0.717870,0.718937,0.720003,0.721066,0.722128,0.723188, 0.724247,0.725304,0.726359,0.727413,0.728464,0.729514,0.730563,0.731609, 0.732654,0.733697,0.734739,0.735779,0.736817,0.737853,0.738887,0.739920, 0.740951,0.741980,0.743008,0.744034,0.745058,0.746080,0.747101,0.748119, 0.749136,0.750152,0.751165,0.752177,0.753187,0.754195,0.755201,0.756206, 0.757209,0.758210,0.759209,0.760207,0.761202,0.762196,0.763188,0.764179, 0.765167,0.766154,0.767139,0.768122,0.769103,0.770083,0.771061,0.772036, 0.773010,0.773983,0.774953,0.775922,0.776888,0.777853,0.778817,0.779778, 0.780737,0.781695,0.782651,0.783605,0.784557,0.785507,0.786455,0.787402, 0.788346,0.789289,0.790230,0.791169,0.792107,0.793042,0.793975,0.794907, 0.795837,0.796765,0.797691,0.798615,0.799537,0.800458,0.801376,0.802293, 0.803208,0.804120,0.805031,0.805940,0.806848,0.807753,0.808656,0.809558, 0.810457,0.811355,0.812251,0.813144,0.814036,0.814926,0.815814,0.816701, 0.817585,0.818467,0.819348,0.820226,0.821103,0.821977,0.822850,0.823721, 0.824589,0.825456,0.826321,0.827184,0.828045,0.828904,0.829761,0.830616, 0.831470,0.832321,0.833170,0.834018,0.834863,0.835706,0.836548,0.837387, 0.838225,0.839060,0.839894,0.840725,0.841555,0.842383,0.843208,0.844032, 0.844854,0.845673,0.846491,0.847307,0.848120,0.848932,0.849742,0.850549, 0.851355,0.852159,0.852961,0.853760,0.854558,0.855354,0.856147,0.856939, 0.857729,0.858516,0.859302,0.860085,0.860867,0.861646,0.862424,0.863199, 0.863973,0.864744,0.865514,0.866281,0.867046,0.867809,0.868571,0.869330, 0.870087,0.870842,0.871595,0.872346,0.873095,0.873842,0.874587,0.875329, 0.876070,0.876809,0.877545,0.878280,0.879012,0.879743,0.880471,0.881197, 0.881921,0.882643,0.883363,0.884081,0.884797,0.885511,0.886223,0.886932, 0.887640,0.888345,0.889048,0.889750,0.890449,0.891146,0.891841,0.892534, 0.893224,0.893913,0.894599,0.895284,0.895966,0.896646,0.897325,0.898001, 0.898674,0.899346,0.900016,0.900683,0.901349,0.902012,0.902673,0.903332, 0.903989,0.904644,0.905297,0.905947,0.906596,0.907242,0.907886,0.908528, 0.909168,0.909806,0.910441,0.911075,0.911706,0.912335,0.912962,0.913587, 0.914210,0.914830,0.915449,0.916065,0.916679,0.917291,0.917901,0.918508, 0.919114,0.919717,0.920318,0.920917,0.921514,0.922109,0.922701,0.923291, 0.923880,0.924465,0.925049,0.925631,0.926210,0.926787,0.927363,0.927935, 0.928506,0.929075,0.929641,0.930205,0.930767,0.931327,0.931884,0.932440, 0.932993,0.933544,0.934093,0.934639,0.935184,0.935726,0.936266,0.936803, 0.937339,0.937872,0.938404,0.938932,0.939459,0.939984,0.940506,0.941026, 0.941544,0.942060,0.942573,0.943084,0.943593,0.944100,0.944605,0.945107, 0.945607,0.946105,0.946601,0.947094,0.947586,0.948075,0.948561,0.949046, 0.949528,0.950008,0.950486,0.950962,0.951435,0.951906,0.952375,0.952842, 0.953306,0.953768,0.954228,0.954686,0.955141,0.955594,0.956045,0.956494, 0.956940,0.957385,0.957826,0.958266,0.958703,0.959139,0.959572,0.960002, 0.960431,0.960857,0.961280,0.961702,0.962121,0.962538,0.962953,0.963366, 0.963776,0.964184,0.964590,0.964993,0.965394,0.965793,0.966190,0.966584, 0.966976,0.967366,0.967754,0.968139,0.968522,0.968903,0.969281,0.969657, 0.970031,0.970403,0.970772,0.971139,0.971504,0.971866,0.972226,0.972584, 0.972940,0.973293,0.973644,0.973993,0.974339,0.974684,0.975025,0.975365, 0.975702,0.976037,0.976370,0.976700,0.977028,0.977354,0.977677,0.977999, 0.978317,0.978634,0.978948,0.979260,0.979570,0.979877,0.980182,0.980485, 0.980785,0.981083,0.981379,0.981673,0.981964,0.982253,0.982539,0.982824, 0.983105,0.983385,0.983662,0.983937,0.984210,0.984480,0.984749,0.985014, 0.985278,0.985539,0.985798,0.986054,0.986308,0.986560,0.986809,0.987057, 0.987301,0.987544,0.987784,0.988022,0.988258,0.988491,0.988722,0.988950, 0.989177,0.989400,0.989622,0.989841,0.990058,0.990273,0.990485,0.990695, 0.990903,0.991108,0.991311,0.991511,0.991710,0.991906,0.992099,0.992291, 0.992480,0.992666,0.992850,0.993032,0.993212,0.993389,0.993564,0.993737, 0.993907,0.994075,0.994240,0.994404,0.994565,0.994723,0.994879,0.995033, 0.995185,0.995334,0.995481,0.995625,0.995767,0.995907,0.996045,0.996180, 0.996313,0.996443,0.996571,0.996697,0.996820,0.996941,0.997060,0.997176, 0.997290,0.997402,0.997511,0.997618,0.997723,0.997825,0.997925,0.998023, 0.998118,0.998211,0.998302,0.998390,0.998476,0.998559,0.998640,0.998719, 0.998795,0.998870,0.998941,0.999011,0.999078,0.999142,0.999205,0.999265, 0.999322,0.999378,0.999431,0.999481,0.999529,0.999575,0.999619,0.999660, 0.999699,0.999735,0.999769,0.999801,0.999831,0.999858,0.999882,0.999905, 0.999925,0.999942,0.999958,0.999971,0.999981,0.999989,0.999995,0.999999 }; double sin( double x ) { int index; int quad; index = 1024 * x / (M_PI * 0.5); quad = ( index >> 10 ) & 3; index &= 1023; switch ( quad ) { case 0: return sintable[index]; case 1: return sintable[1023-index]; case 2: return -sintable[index]; case 3: return -sintable[1023-index]; } return 0; } double cos( double x ) { int index; int quad; index = 1024 * x / (M_PI * 0.5); quad = ( index >> 10 ) & 3; index &= 1023; switch ( quad ) { case 3: return sintable[index]; case 0: return sintable[1023-index]; case 1: return -sintable[index]; case 2: return -sintable[1023-index]; } return 0; } /* void create_acostable( void ) { int i; FILE *fp; float a; fp = fopen("c:\\acostable.txt", "w"); fprintf(fp, "float acostable[] = {"); for (i = 0; i < 1024; i++) { if (!(i & 7)) fprintf(fp, "\n"); a = acos( (float) -1 + i / 512 ); fprintf(fp, "%1.8f,", a); } fprintf(fp, "\n}\n"); fclose(fp); } */ float acostable[] = { 3.14159265,3.07908248,3.05317551,3.03328655,3.01651113,3.00172442,2.98834964,2.97604422, 2.96458497,2.95381690,2.94362719,2.93393068,2.92466119,2.91576615,2.90720289,2.89893629, 2.89093699,2.88318015,2.87564455,2.86831188,2.86116621,2.85419358,2.84738169,2.84071962, 2.83419760,2.82780691,2.82153967,2.81538876,2.80934770,2.80341062,2.79757211,2.79182724, 2.78617145,2.78060056,2.77511069,2.76969824,2.76435988,2.75909250,2.75389319,2.74875926, 2.74368816,2.73867752,2.73372510,2.72882880,2.72398665,2.71919677,2.71445741,2.70976688, 2.70512362,2.70052613,2.69597298,2.69146283,2.68699438,2.68256642,2.67817778,2.67382735, 2.66951407,2.66523692,2.66099493,2.65678719,2.65261279,2.64847088,2.64436066,2.64028133, 2.63623214,2.63221238,2.62822133,2.62425835,2.62032277,2.61641398,2.61253138,2.60867440, 2.60484248,2.60103507,2.59725167,2.59349176,2.58975488,2.58604053,2.58234828,2.57867769, 2.57502832,2.57139977,2.56779164,2.56420354,2.56063509,2.55708594,2.55355572,2.55004409, 2.54655073,2.54307530,2.53961750,2.53617701,2.53275354,2.52934680,2.52595650,2.52258238, 2.51922417,2.51588159,2.51255441,2.50924238,2.50594525,2.50266278,2.49939476,2.49614096, 2.49290115,2.48967513,2.48646269,2.48326362,2.48007773,2.47690482,2.47374472,2.47059722, 2.46746215,2.46433933,2.46122860,2.45812977,2.45504269,2.45196720,2.44890314,2.44585034, 2.44280867,2.43977797,2.43675809,2.43374890,2.43075025,2.42776201,2.42478404,2.42181622, 2.41885841,2.41591048,2.41297232,2.41004380,2.40712480,2.40421521,2.40131491,2.39842379, 2.39554173,2.39266863,2.38980439,2.38694889,2.38410204,2.38126374,2.37843388,2.37561237, 2.37279910,2.36999400,2.36719697,2.36440790,2.36162673,2.35885335,2.35608768,2.35332964, 2.35057914,2.34783610,2.34510044,2.34237208,2.33965094,2.33693695,2.33423003,2.33153010, 2.32883709,2.32615093,2.32347155,2.32079888,2.31813284,2.31547337,2.31282041,2.31017388, 2.30753373,2.30489988,2.30227228,2.29965086,2.29703556,2.29442632,2.29182309,2.28922580, 2.28663439,2.28404881,2.28146900,2.27889490,2.27632647,2.27376364,2.27120637,2.26865460, 2.26610827,2.26356735,2.26103177,2.25850149,2.25597646,2.25345663,2.25094195,2.24843238, 2.24592786,2.24342836,2.24093382,2.23844420,2.23595946,2.23347956,2.23100444,2.22853408, 2.22606842,2.22360742,2.22115104,2.21869925,2.21625199,2.21380924,2.21137096,2.20893709, 2.20650761,2.20408248,2.20166166,2.19924511,2.19683280,2.19442469,2.19202074,2.18962092, 2.18722520,2.18483354,2.18244590,2.18006225,2.17768257,2.17530680,2.17293493,2.17056692, 2.16820274,2.16584236,2.16348574,2.16113285,2.15878367,2.15643816,2.15409630,2.15175805, 2.14942338,2.14709226,2.14476468,2.14244059,2.14011997,2.13780279,2.13548903,2.13317865, 2.13087163,2.12856795,2.12626757,2.12397047,2.12167662,2.11938600,2.11709859,2.11481435, 2.11253326,2.11025530,2.10798044,2.10570867,2.10343994,2.10117424,2.09891156,2.09665185, 2.09439510,2.09214129,2.08989040,2.08764239,2.08539725,2.08315496,2.08091550,2.07867884, 2.07644495,2.07421383,2.07198545,2.06975978,2.06753681,2.06531651,2.06309887,2.06088387, 2.05867147,2.05646168,2.05425445,2.05204979,2.04984765,2.04764804,2.04545092,2.04325628, 2.04106409,2.03887435,2.03668703,2.03450211,2.03231957,2.03013941,2.02796159,2.02578610, 2.02361292,2.02144204,2.01927344,2.01710710,2.01494300,2.01278113,2.01062146,2.00846399, 2.00630870,2.00415556,2.00200457,1.99985570,1.99770895,1.99556429,1.99342171,1.99128119, 1.98914271,1.98700627,1.98487185,1.98273942,1.98060898,1.97848051,1.97635399,1.97422942, 1.97210676,1.96998602,1.96786718,1.96575021,1.96363511,1.96152187,1.95941046,1.95730088, 1.95519310,1.95308712,1.95098292,1.94888050,1.94677982,1.94468089,1.94258368,1.94048818, 1.93839439,1.93630228,1.93421185,1.93212308,1.93003595,1.92795046,1.92586659,1.92378433, 1.92170367,1.91962459,1.91754708,1.91547113,1.91339673,1.91132385,1.90925250,1.90718266, 1.90511432,1.90304746,1.90098208,1.89891815,1.89685568,1.89479464,1.89273503,1.89067683, 1.88862003,1.88656463,1.88451060,1.88245794,1.88040664,1.87835668,1.87630806,1.87426076, 1.87221477,1.87017008,1.86812668,1.86608457,1.86404371,1.86200412,1.85996577,1.85792866, 1.85589277,1.85385809,1.85182462,1.84979234,1.84776125,1.84573132,1.84370256,1.84167495, 1.83964848,1.83762314,1.83559892,1.83357582,1.83155381,1.82953289,1.82751305,1.82549429, 1.82347658,1.82145993,1.81944431,1.81742973,1.81541617,1.81340362,1.81139207,1.80938151, 1.80737194,1.80536334,1.80335570,1.80134902,1.79934328,1.79733848,1.79533460,1.79333164, 1.79132959,1.78932843,1.78732817,1.78532878,1.78333027,1.78133261,1.77933581,1.77733985, 1.77534473,1.77335043,1.77135695,1.76936428,1.76737240,1.76538132,1.76339101,1.76140148, 1.75941271,1.75742470,1.75543743,1.75345090,1.75146510,1.74948002,1.74749565,1.74551198, 1.74352900,1.74154672,1.73956511,1.73758417,1.73560389,1.73362426,1.73164527,1.72966692, 1.72768920,1.72571209,1.72373560,1.72175971,1.71978441,1.71780969,1.71583556,1.71386199, 1.71188899,1.70991653,1.70794462,1.70597325,1.70400241,1.70203209,1.70006228,1.69809297, 1.69612416,1.69415584,1.69218799,1.69022062,1.68825372,1.68628727,1.68432127,1.68235571, 1.68039058,1.67842588,1.67646160,1.67449772,1.67253424,1.67057116,1.66860847,1.66664615, 1.66468420,1.66272262,1.66076139,1.65880050,1.65683996,1.65487975,1.65291986,1.65096028, 1.64900102,1.64704205,1.64508338,1.64312500,1.64116689,1.63920905,1.63725148,1.63529416, 1.63333709,1.63138026,1.62942366,1.62746728,1.62551112,1.62355517,1.62159943,1.61964388, 1.61768851,1.61573332,1.61377831,1.61182346,1.60986877,1.60791422,1.60595982,1.60400556, 1.60205142,1.60009739,1.59814349,1.59618968,1.59423597,1.59228235,1.59032882,1.58837536, 1.58642196,1.58446863,1.58251535,1.58056211,1.57860891,1.57665574,1.57470259,1.57274945, 1.57079633,1.56884320,1.56689007,1.56493692,1.56298375,1.56103055,1.55907731,1.55712403, 1.55517069,1.55321730,1.55126383,1.54931030,1.54735668,1.54540297,1.54344917,1.54149526, 1.53954124,1.53758710,1.53563283,1.53367843,1.53172389,1.52976919,1.52781434,1.52585933, 1.52390414,1.52194878,1.51999323,1.51803748,1.51608153,1.51412537,1.51216900,1.51021240, 1.50825556,1.50629849,1.50434117,1.50238360,1.50042576,1.49846765,1.49650927,1.49455060, 1.49259163,1.49063237,1.48867280,1.48671291,1.48475270,1.48279215,1.48083127,1.47887004, 1.47690845,1.47494650,1.47298419,1.47102149,1.46905841,1.46709493,1.46513106,1.46316677, 1.46120207,1.45923694,1.45727138,1.45530538,1.45333893,1.45137203,1.44940466,1.44743682, 1.44546850,1.44349969,1.44153038,1.43956057,1.43759024,1.43561940,1.43364803,1.43167612, 1.42970367,1.42773066,1.42575709,1.42378296,1.42180825,1.41983295,1.41785705,1.41588056, 1.41390346,1.41192573,1.40994738,1.40796840,1.40598877,1.40400849,1.40202755,1.40004594, 1.39806365,1.39608068,1.39409701,1.39211264,1.39012756,1.38814175,1.38615522,1.38416795, 1.38217994,1.38019117,1.37820164,1.37621134,1.37422025,1.37222837,1.37023570,1.36824222, 1.36624792,1.36425280,1.36225684,1.36026004,1.35826239,1.35626387,1.35426449,1.35226422, 1.35026307,1.34826101,1.34625805,1.34425418,1.34224937,1.34024364,1.33823695,1.33622932, 1.33422072,1.33221114,1.33020059,1.32818904,1.32617649,1.32416292,1.32214834,1.32013273, 1.31811607,1.31609837,1.31407960,1.31205976,1.31003885,1.30801684,1.30599373,1.30396951, 1.30194417,1.29991770,1.29789009,1.29586133,1.29383141,1.29180031,1.28976803,1.28773456, 1.28569989,1.28366400,1.28162688,1.27958854,1.27754894,1.27550809,1.27346597,1.27142257, 1.26937788,1.26733189,1.26528459,1.26323597,1.26118602,1.25913471,1.25708205,1.25502803, 1.25297262,1.25091583,1.24885763,1.24679802,1.24473698,1.24267450,1.24061058,1.23854519, 1.23647833,1.23440999,1.23234015,1.23026880,1.22819593,1.22612152,1.22404557,1.22196806, 1.21988898,1.21780832,1.21572606,1.21364219,1.21155670,1.20946958,1.20738080,1.20529037, 1.20319826,1.20110447,1.19900898,1.19691177,1.19481283,1.19271216,1.19060973,1.18850553, 1.18639955,1.18429178,1.18218219,1.18007079,1.17795754,1.17584244,1.17372548,1.17160663, 1.16948589,1.16736324,1.16523866,1.16311215,1.16098368,1.15885323,1.15672081,1.15458638, 1.15244994,1.15031147,1.14817095,1.14602836,1.14388370,1.14173695,1.13958808,1.13743709, 1.13528396,1.13312866,1.13097119,1.12881153,1.12664966,1.12448556,1.12231921,1.12015061, 1.11797973,1.11580656,1.11363107,1.11145325,1.10927308,1.10709055,1.10490563,1.10271831, 1.10052856,1.09833638,1.09614174,1.09394462,1.09174500,1.08954287,1.08733820,1.08513098, 1.08292118,1.08070879,1.07849378,1.07627614,1.07405585,1.07183287,1.06960721,1.06737882, 1.06514770,1.06291382,1.06067715,1.05843769,1.05619540,1.05395026,1.05170226,1.04945136, 1.04719755,1.04494080,1.04268110,1.04041841,1.03815271,1.03588399,1.03361221,1.03133735, 1.02905939,1.02677830,1.02449407,1.02220665,1.01991603,1.01762219,1.01532509,1.01302471, 1.01072102,1.00841400,1.00610363,1.00378986,1.00147268,0.99915206,0.99682798,0.99450039, 0.99216928,0.98983461,0.98749636,0.98515449,0.98280898,0.98045980,0.97810691,0.97575030, 0.97338991,0.97102573,0.96865772,0.96628585,0.96391009,0.96153040,0.95914675,0.95675912, 0.95436745,0.95197173,0.94957191,0.94716796,0.94475985,0.94234754,0.93993099,0.93751017, 0.93508504,0.93265556,0.93022170,0.92778341,0.92534066,0.92289341,0.92044161,0.91798524, 0.91552424,0.91305858,0.91058821,0.90811309,0.90563319,0.90314845,0.90065884,0.89816430, 0.89566479,0.89316028,0.89065070,0.88813602,0.88561619,0.88309116,0.88056088,0.87802531, 0.87548438,0.87293806,0.87038629,0.86782901,0.86526619,0.86269775,0.86012366,0.85754385, 0.85495827,0.85236686,0.84976956,0.84716633,0.84455709,0.84194179,0.83932037,0.83669277, 0.83405893,0.83141877,0.82877225,0.82611928,0.82345981,0.82079378,0.81812110,0.81544172, 0.81275556,0.81006255,0.80736262,0.80465570,0.80194171,0.79922057,0.79649221,0.79375655, 0.79101352,0.78826302,0.78550497,0.78273931,0.77996593,0.77718475,0.77439569,0.77159865, 0.76879355,0.76598029,0.76315878,0.76032891,0.75749061,0.75464376,0.75178826,0.74892402, 0.74605092,0.74316887,0.74027775,0.73737744,0.73446785,0.73154885,0.72862033,0.72568217, 0.72273425,0.71977644,0.71680861,0.71383064,0.71084240,0.70784376,0.70483456,0.70181469, 0.69878398,0.69574231,0.69268952,0.68962545,0.68654996,0.68346288,0.68036406,0.67725332, 0.67413051,0.67099544,0.66784794,0.66468783,0.66151492,0.65832903,0.65512997,0.65191753, 0.64869151,0.64545170,0.64219789,0.63892987,0.63564741,0.63235028,0.62903824,0.62571106, 0.62236849,0.61901027,0.61563615,0.61224585,0.60883911,0.60541564,0.60197515,0.59851735, 0.59504192,0.59154856,0.58803694,0.58450672,0.58095756,0.57738911,0.57380101,0.57019288, 0.56656433,0.56291496,0.55924437,0.55555212,0.55183778,0.54810089,0.54434099,0.54055758, 0.53675018,0.53291825,0.52906127,0.52517867,0.52126988,0.51733431,0.51337132,0.50938028, 0.50536051,0.50131132,0.49723200,0.49312177,0.48897987,0.48480547,0.48059772,0.47635573, 0.47207859,0.46776530,0.46341487,0.45902623,0.45459827,0.45012983,0.44561967,0.44106652, 0.43646903,0.43182577,0.42713525,0.42239588,0.41760600,0.41276385,0.40786755,0.40291513, 0.39790449,0.39283339,0.38769946,0.38250016,0.37723277,0.37189441,0.36648196,0.36099209, 0.35542120,0.34976542,0.34402054,0.33818204,0.33224495,0.32620390,0.32005298,0.31378574, 0.30739505,0.30087304,0.29421096,0.28739907,0.28042645,0.27328078,0.26594810,0.25841250, 0.25065566,0.24265636,0.23438976,0.22582651,0.21693146,0.20766198,0.19796546,0.18777575, 0.17700769,0.16554844,0.15324301,0.13986823,0.12508152,0.10830610,0.08841715,0.06251018, } double acos( double x ) { int index; if (x < -1) x = -1; if (x > 1) x = 1; index = (float) (1.0 + x) * 511.9; return acostable[index]; } double atan2( double y, double x ) { float base; float temp; float dir; float test; int i; if ( x < 0 ) { if ( y >= 0 ) { // quad 1 base = M_PI / 2; temp = x; x = y; y = -temp; } else { // quad 2 base = M_PI; x = -x; y = -y; } } else { if ( y < 0 ) { // quad 3 base = 3 * M_PI / 2; temp = x; x = -y; y = temp; } } if ( y > x ) { base += M_PI/2; temp = x; x = y; y = temp; dir = -1; } else { dir = 1; } // calcualte angle in octant 0 if ( x == 0 ) { return base; } y /= x; for ( i = 0 ; i < 512 ; i++ ) { test = sintable[i] / sintable[1023-i]; if ( test > y ) { break; } } return base + dir * i * ( M_PI/2048); } #endif double tan( double x ) { return sin(x) / cos(x); } static int randSeed = 0; void srand( unsigned seed ) { randSeed = seed; } int rand( void ) { randSeed = (69069 * randSeed + 1); return randSeed & 0x7fff; } double atof( const char *string ) { float sign; float value; int c; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; c = string[0]; if ( c != '.' ) { do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); } else { string++; } // check for decimal point if ( c == '.' ) { double fraction; fraction = 0.1; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value += c * fraction; fraction *= 0.1; } while ( 1 ); } // not handling 10e10 notation... return value * sign; } double _atof( const char **stringPtr ) { const char *string; float sign; float value; int c = '0'; string = *stringPtr; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { *stringPtr = string; return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; if ( string[0] != '.' ) { do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); } // check for decimal point if ( c == '.' ) { double fraction; fraction = 0.1; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value += c * fraction; fraction *= 0.1; } while ( 1 ); } // not handling 10e10 notation... *stringPtr = string; return value * sign; } /* ============== strtod Without an errno variable, this is a fair bit less useful than it is in libc but it's still a fair bit more capable than atof or _atof Handles inf[inity], nan (ignoring case), hexadecimals, and decimals Handles decimal exponents like 10e10 and hex exponents like 0x7f8p20 10e10 == 10000000000 (power of ten) 0x7f8p20 == 0x7f800000 (decimal power of two) The variable pointed to by endptr will hold the location of the first character in the nptr string that was not used in the conversion ============== */ double strtod( const char *nptr, const char **endptr ) { double res; qboolean neg = qfalse; // skip whitespace while( isspace( *nptr ) ) nptr++; // special string parsing if( Q_stricmpn( nptr, "nan", 3 ) == 0 ) { floatint_t nan; if( endptr == NULL ) { nan.ui = 0x7fffffff; return nan.f; } *endptr = &nptr[3]; // nan can be followed by a bracketed number (in hex, octal, // or decimal) which is then put in the mantissa // this can be used to generate signalling or quiet NaNs, for // example (though I doubt it'll ever be used) // note that nan(0) is infinity! if( nptr[3] == '(' ) { const char *end; int mantissa = strtol( &nptr[4], &end, 0 ); if( *end == ')' ) { nan.ui = 0x7f800000 | ( mantissa & 0x7fffff ); if( endptr ) *endptr = &end[1]; return nan.f; } } nan.ui = 0x7fffffff; return nan.f; } if( Q_stricmpn( nptr, "inf", 3 ) == 0 ) { floatint_t inf; inf.ui = 0x7f800000; if( endptr == NULL ) return inf.f; if( Q_stricmpn( &nptr[3], "inity", 5 ) == 0 ) *endptr = &nptr[8]; else *endptr = &nptr[3]; return inf.f; } // normal numeric parsing // sign if( *nptr == '-' ) { nptr++; neg = qtrue; } else if( *nptr == '+' ) nptr++; // hex if( Q_stricmpn( nptr, "0x", 2 ) == 0 ) { // track if we use any digits const char *s = &nptr[1], *end = s; nptr += 2; res = 0; while( qtrue ) { if( isdigit( *nptr ) ) res = 16 * res + ( *nptr++ - '0' ); else if( *nptr >= 'A' && *nptr <= 'F' ) res = 16 * res + 10 + *nptr++ - 'A'; else if( *nptr >= 'a' && *nptr <= 'f' ) res = 16 * res + 10 + *nptr++ - 'a'; else break; } // if nptr moved, save it if( end + 1 < nptr ) end = nptr; if( *nptr == '.' ) { float place; nptr++; // 1.0 / 16.0 == 0.0625 // I don't expect the float accuracy to hold out for // very long but since we need to know the length of // the string anyway we keep on going regardless for( place = 0.0625;; place /= 16.0 ) { if( isdigit( *nptr ) ) res += place * ( *nptr++ - '0' ); else if( *nptr >= 'A' && *nptr <= 'F' ) res += place * ( 10 + *nptr++ - 'A' ); else if( *nptr >= 'a' && *nptr <= 'f' ) res += place * ( 10 + *nptr++ - 'a' ); else break; } if( end < nptr ) end = nptr; } // parse an optional exponent, representing multiplication // by a power of two // exponents are only valid if we encountered at least one // digit already (and have therefore set end to something) if( end != s && tolower( *nptr ) == 'p' ) { int exp; float res2; // apparently (confusingly) the exponent should be // decimal exp = strtol( &nptr[1], &end, 10 ); if( &nptr[1] == end ) { // no exponent if( endptr ) *endptr = nptr; return res; } if( exp > 0 ) { while( exp-- > 0 ) { res2 = res * 2; // check for infinity if( res2 <= res ) break; res = res2; } } else { while( exp++ < 0 ) { res2 = res / 2; // check for underflow if( res2 >= res ) break; res = res2; } } } if( endptr ) *endptr = end; return res; } // decimal else { // track if we find any digits const char *end = nptr, *p = nptr; // this is most of the work for( res = 0; isdigit( *nptr ); res = 10 * res + *nptr++ - '0' ); // if nptr moved, we read something if( end < nptr ) end = nptr; if( *nptr == '.' ) { // fractional part float place; nptr++; for( place = 0.1; isdigit( *nptr ); place /= 10.0 ) res += ( *nptr++ - '0' ) * place; // if nptr moved, we read something if( end + 1 < nptr ) end = nptr; } // exponent // meaningless without having already read digits, so check // we've set end to something if( p != end && tolower( *nptr ) == 'e' ) { int exp; float res10; exp = strtol( &nptr[1], &end, 10 ); if( &nptr[1] == end ) { // no exponent if( endptr ) *endptr = nptr; return res; } if( exp > 0 ) { while( exp-- > 0 ) { res10 = res * 10; // check for infinity to save us time if( res10 <= res ) break; res = res10; } } else if( exp < 0 ) { while( exp++ < 0 ) { res10 = res / 10; // check for underflow // (test for 0 would probably be just // as good) if( res10 >= res ) break; res = res10; } } } if( endptr ) *endptr = end; return res; } } int atoi( const char *string ) { int sign; int value; int c; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); // not handling 10e10 notation... return value * sign; } int _atoi( const char **stringPtr ) { int sign; int value; int c; const char *string; string = *stringPtr; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); // not handling 10e10 notation... *stringPtr = string; return value * sign; } /* ============== strtol Handles any base from 2 to 36. If base is 0 then it guesses decimal, hex, or octal based on the format of the number (leading 0 or 0x) Will not overflow - returns LONG_MIN or LONG_MAX as appropriate *endptr is set to the location of the first character not used ============== */ long strtol( const char *nptr, const char **endptr, int base ) { long res; qboolean pos = qtrue; if( endptr ) *endptr = nptr; // bases other than 0, 2, 8, 16 are very rarely used, but they're // not much extra effort to support if( base < 0 || base == 1 || base > 36 ) return 0; // skip leading whitespace while( isspace( *nptr ) ) nptr++; // sign if( *nptr == '-' ) { nptr++; pos = qfalse; } else if( *nptr == '+' ) nptr++; // look for base-identifying sequences e.g. 0x for hex, 0 for octal if( nptr[0] == '0' ) { nptr++; // 0 is always a valid digit if( endptr ) *endptr = nptr; if( *nptr == 'x' || *nptr == 'X' ) { if( base != 0 && base != 16 ) { // can't be hex, reject x (accept 0) if( endptr ) *endptr = nptr; return 0; } nptr++; base = 16; } else if( base == 0 ) base = 8; } else if( base == 0 ) base = 10; res = 0; while( qtrue ) { int val; if( isdigit( *nptr ) ) val = *nptr - '0'; else if( islower( *nptr ) ) val = 10 + *nptr - 'a'; else if( isupper( *nptr ) ) val = 10 + *nptr - 'A'; else break; if( val >= base ) break; // we go negative because LONG_MIN is further from 0 than // LONG_MAX if( res < ( LONG_MIN + val ) / base ) res = LONG_MIN; // overflow else res = res * base - val; nptr++; if( endptr ) *endptr = nptr; } if( pos ) { // can't represent LONG_MIN positive if( res == LONG_MIN ) res = LONG_MAX; else res = -res; } return res; } int abs( int n ) { return n < 0 ? -n : n; } double fabs( double x ) { return x < 0 ? -x : x; } //========================================================= /* * New implementation by Patrick Powell and others for vsnprintf. * Supports length checking in strings. */ /* * Copyright Patrick Powell 1995 * This code is based on code written by Patrick Powell (papowell@astart.com) * It may be used for any purpose as long as this notice remains intact * on all source code distributions */ /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 * A bombproof version of doprnt (dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. * * More Recently: * Brandon Long 9/15/96 for mutt 0.43 * This was ugly. It is still ugly. I opted out of floating point * numbers, but the formatter understands just about everything * from the normal C string format, at least as far as I can tell from * the Solaris 2.5 printf(3S) man page. * * Brandon Long 10/22/97 for mutt 0.87.1 * Ok, added some minimal floating point support, which means this * probably requires libm on most operating systems. Don't yet * support the exponent (e,E) and sigfig (g,G). Also, fmtint() * was pretty badly broken, it just wasn't being exercised in ways * which showed it, so that's been fixed. Also, formated the code * to mutt conventions, and removed dead code left over from the * original. Also, there is now a builtin-test, just compile with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm * and run snprintf for results. * * Thomas Roessler 01/27/98 for mutt 0.89i * The PGP code was using unsigned hexadecimal formats. * Unfortunately, unsigned formats simply didn't work. * * Michael Elkins 03/05/98 for mutt 0.90.8 * The original code assumed that both snprintf() and vsnprintf() were * missing. Some systems only have snprintf() but not vsnprintf(), so * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. * * Andrew Tridgell (tridge@samba.org) Oct 1998 * fixed handling of %.0f * added test for HAVE_LONG_DOUBLE * * Russ Allbery 2000-08-26 * fixed return value to comply with C99 * fixed handling of snprintf(NULL, ...) * * Hrvoje Niksic 2000-11-04 * include instead of "config.h". * moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef. * include for NULL. * added support and test cases for long long. * don't declare argument types to (v)snprintf if stdarg is not used. * use int instead of short int as 2nd arg to va_arg. * **************************************************************/ /* BDR 2002-01-13 %e and %g were being ignored. Now do something, if not necessarily correctly */ #if (SIZEOF_LONG_DOUBLE > 0) /* #ifdef HAVE_LONG_DOUBLE */ #define LDOUBLE long double #else #define LDOUBLE double #endif #if (SIZEOF_LONG_LONG > 0) /* #ifdef HAVE_LONG_LONG */ # define LLONG long long #else # define LLONG long #endif static int dopr (char *buffer, size_t maxlen, const char *format, va_list args); static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, int min, int max); static int fmtint (char *buffer, size_t *currlen, size_t maxlen, LLONG value, int base, int min, int max, int flags); static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags); static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); /* * dopr(): poor man's version of doprintf */ /* format read states */ #define DP_S_DEFAULT 0 #define DP_S_FLAGS 1 #define DP_S_MIN 2 #define DP_S_DOT 3 #define DP_S_MAX 4 #define DP_S_MOD 5 #define DP_S_MOD_L 6 #define DP_S_CONV 7 #define DP_S_DONE 8 /* format flags - Bits */ #define DP_F_MINUS (1 << 0) #define DP_F_PLUS (1 << 1) #define DP_F_SPACE (1 << 2) #define DP_F_NUM (1 << 3) #define DP_F_ZERO (1 << 4) #define DP_F_UP (1 << 5) #define DP_F_UNSIGNED (1 << 6) /* Conversion Flags */ #define DP_C_SHORT 1 #define DP_C_LONG 2 #define DP_C_LLONG 3 #define DP_C_LDOUBLE 4 #define char_to_int(p) (p - '0') #define MAX(p,q) ((p >= q) ? p : q) #define MIN(p,q) ((p <= q) ? p : q) static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) { char ch; LLONG value; LDOUBLE fvalue; char *strvalue; int min; int max; int state; int flags; int cflags; int total; size_t currlen; state = DP_S_DEFAULT; currlen = flags = cflags = min = 0; max = -1; ch = *format++; total = 0; while (state != DP_S_DONE) { if (ch == '\0') state = DP_S_DONE; switch(state) { case DP_S_DEFAULT: if (ch == '%') state = DP_S_FLAGS; else total += dopr_outch (buffer, &currlen, maxlen, ch); ch = *format++; break; case DP_S_FLAGS: switch (ch) { case '-': flags |= DP_F_MINUS; ch = *format++; break; case '+': flags |= DP_F_PLUS; ch = *format++; break; case ' ': flags |= DP_F_SPACE; ch = *format++; break; case '#': flags |= DP_F_NUM; ch = *format++; break; case '0': flags |= DP_F_ZERO; ch = *format++; break; default: state = DP_S_MIN; break; } break; case DP_S_MIN: if ('0' <= ch && ch <= '9') { min = 10*min + char_to_int (ch); ch = *format++; } else if (ch == '*') { min = va_arg (args, int); ch = *format++; state = DP_S_DOT; } else state = DP_S_DOT; break; case DP_S_DOT: if (ch == '.') { state = DP_S_MAX; ch = *format++; } else state = DP_S_MOD; break; case DP_S_MAX: if ('0' <= ch && ch <= '9') { if (max < 0) max = 0; max = 10*max + char_to_int (ch); ch = *format++; } else if (ch == '*') { max = va_arg (args, int); ch = *format++; state = DP_S_MOD; } else state = DP_S_MOD; break; case DP_S_MOD: switch (ch) { case 'h': cflags = DP_C_SHORT; ch = *format++; break; case 'l': cflags = DP_C_LONG; ch = *format++; break; case 'L': cflags = DP_C_LDOUBLE; ch = *format++; break; default: break; } if (cflags != DP_C_LONG) state = DP_S_CONV; else state = DP_S_MOD_L; break; case DP_S_MOD_L: switch (ch) { case 'l': cflags = DP_C_LLONG; ch = *format++; break; default: break; } state = DP_S_CONV; break; case DP_S_CONV: switch (ch) { case 'd': case 'i': if (cflags == DP_C_SHORT) value = (short int)va_arg (args, int); else if (cflags == DP_C_LONG) value = va_arg (args, long int); else if (cflags == DP_C_LLONG) value = va_arg (args, LLONG); else value = va_arg (args, int); total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) // value = (unsigned short int) va_arg (args, unsigned short int); // Thilo: This does not work because the rcc compiler cannot do that cast correctly. value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); // Using this workaround instead. else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); break; case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'X': flags |= DP_F_UP; case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) value = va_arg (args, unsigned int) & ( (1 << sizeof(unsigned short int) * 8) - 1); else if (cflags == DP_C_LONG) value = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) value = va_arg (args, unsigned LLONG); else value = va_arg (args, unsigned int); total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); break; case 'f': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); /* um, floating point? */ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'E': flags |= DP_F_UP; case 'e': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); /* um, floating point? */ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'G': flags |= DP_F_UP; case 'g': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); /* um, floating point? */ total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'c': total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); break; case 's': strvalue = va_arg (args, char *); total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); break; case 'p': strvalue = va_arg (args, void *); total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) { short int *num; num = va_arg (args, short int *); *num = currlen; } else if (cflags == DP_C_LONG) { long int *num; num = va_arg (args, long int *); *num = currlen; } else if (cflags == DP_C_LLONG) { LLONG *num; num = va_arg (args, LLONG *); *num = currlen; } else { int *num; num = va_arg (args, int *); *num = currlen; } break; case '%': total += dopr_outch (buffer, &currlen, maxlen, ch); break; case 'w': /* not supported yet, treat as next char */ ch = *format++; break; default: /* Unknown, skip */ break; } ch = *format++; state = DP_S_DEFAULT; flags = cflags = min = 0; max = -1; break; case DP_S_DONE: break; default: /* hmm? */ break; /* some picky compilers need this */ } } if (buffer != NULL) { if (currlen < maxlen - 1) buffer[currlen] = '\0'; else buffer[maxlen - 1] = '\0'; } return total; } static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, int min, int max) { int padlen, strln; /* amount to pad */ int cnt = 0; int total = 0; if (value == 0) { value = ""; } for (strln = 0; value[strln]; ++strln); /* strlen */ if (max >= 0 && max < strln) strln = max; padlen = min - strln; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --padlen; } while (*value && ((max < 0) || (cnt < max))) { total += dopr_outch (buffer, currlen, maxlen, *value++); ++cnt; } while (padlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; } return total; } /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ static int fmtint (char *buffer, size_t *currlen, size_t maxlen, LLONG value, int base, int min, int max, int flags) { int signvalue = 0; unsigned LLONG uvalue; char convert[24]; int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ const char *digits; int total = 0; if (max < 0) max = 0; uvalue = value; if(!(flags & DP_F_UNSIGNED)) { if( value < 0 ) { signvalue = '-'; uvalue = -value; } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; } if (flags & DP_F_UP) /* Should characters be upper case? */ digits = "0123456789ABCDEF"; else digits = "0123456789abcdef"; do { convert[place++] = digits[uvalue % (unsigned)base]; uvalue = (uvalue / (unsigned)base ); } while(uvalue && (place < sizeof (convert))); if (place == sizeof (convert)) place--; convert[place] = 0; zpadlen = max - place; spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); if (zpadlen < 0) zpadlen = 0; if (spadlen < 0) spadlen = 0; if (flags & DP_F_ZERO) { zpadlen = MAX(zpadlen, spadlen); spadlen = 0; } if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */ #ifdef DEBUG_SNPRINTF dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", zpadlen, spadlen, min, max, place)); #endif /* Spaces */ while (spadlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --spadlen; } /* Sign */ if (signvalue) total += dopr_outch (buffer, currlen, maxlen, signvalue); /* Zeros */ if (zpadlen > 0) { while (zpadlen > 0) { total += dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } } /* Digits */ while (place > 0) total += dopr_outch (buffer, currlen, maxlen, convert[--place]); /* Left Justified spaces */ while (spadlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++spadlen; } return total; } static LDOUBLE abs_val (LDOUBLE value) { LDOUBLE result = value; if (value < 0) result = -value; return result; } static LDOUBLE pow10 (int exp) { LDOUBLE result = 1; while (exp) { result *= 10; exp--; } return result; } static long round (LDOUBLE value) { long intpart; intpart = value; value = value - intpart; if (value >= 0.5) intpart++; return intpart; } static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags) { int signvalue = 0; LDOUBLE ufvalue; char iconvert[20]; char fconvert[20]; int iplace = 0; int fplace = 0; int padlen = 0; /* amount to pad */ int zpadlen = 0; int caps = 0; int total = 0; long intpart; long fracpart; /* * AIX manpage says the default is 0, but Solaris says the default * is 6, and sprintf on AIX defaults to 6 */ if (max < 0) max = 6; ufvalue = abs_val (fvalue); if (fvalue < 0) signvalue = '-'; else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; #if 0 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ #endif intpart = ufvalue; /* * Sorry, we only support 9 digits past the decimal because of our * conversion method */ if (max > 9) max = 9; /* We "cheat" by converting the fractional part to integer by * multiplying by a factor of 10 */ fracpart = round ((pow10 (max)) * (ufvalue - intpart)); if (fracpart >= pow10 (max)) { intpart++; fracpart -= pow10 (max); } #ifdef DEBUG_SNPRINTF dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); #endif /* Convert integer part */ do { iconvert[iplace++] = (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; intpart = (intpart / 10); } while(intpart && (iplace < 20)); if (iplace == 20) iplace--; iconvert[iplace] = 0; /* Convert fractional part */ do { fconvert[fplace++] = (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; fracpart = (fracpart / 10); } while(fracpart && (fplace < 20)); if (fplace == 20) fplace--; fconvert[fplace] = 0; /* -1 for decimal point, another -1 if we are printing a sign */ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); zpadlen = max - fplace; if (zpadlen < 0) zpadlen = 0; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justifty */ if ((flags & DP_F_ZERO) && (padlen > 0)) { if (signvalue) { total += dopr_outch (buffer, currlen, maxlen, signvalue); --padlen; signvalue = 0; } while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, '0'); --padlen; } } while (padlen > 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); --padlen; } if (signvalue) total += dopr_outch (buffer, currlen, maxlen, signvalue); while (iplace > 0) total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); /* * Decimal point. This should probably use locale to find the correct * char to print out. */ if (max > 0) { total += dopr_outch (buffer, currlen, maxlen, '.'); while (zpadlen-- > 0) total += dopr_outch (buffer, currlen, maxlen, '0'); while (fplace > 0) total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); } while (padlen < 0) { total += dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; } return total; } static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) { if (*currlen + 1 < maxlen) buffer[(*currlen)++] = c; return 1; } int Q_vsnprintf(char *str, size_t length, const char *fmt, va_list args) { if (str != NULL) str[0] = 0; return dopr(str, length, fmt, args); } int Q_snprintf(char *str, size_t length, const char *fmt, ...) { va_list ap; int retval; va_start(ap, fmt); retval = Q_vsnprintf(str, length, fmt, ap); va_end(ap); return retval; } /* this is really crappy */ int sscanf( const char *buffer, const char *fmt, ... ) { int cmd; va_list ap; int count; size_t len; va_start (ap, fmt); count = 0; while ( *fmt ) { if ( fmt[0] != '%' ) { fmt++; continue; } fmt++; cmd = *fmt; if (isdigit (cmd)) { len = (size_t)_atoi (&fmt); cmd = *(fmt - 1); } else { len = MAX_STRING_CHARS - 1; fmt++; } switch ( cmd ) { case 'i': case 'd': case 'u': *(va_arg (ap, int *)) = _atoi( &buffer ); break; case 'f': *(va_arg (ap, float *)) = _atof( &buffer ); break; case 's': { char *s = va_arg (ap, char *); while (isspace (*buffer)) buffer++; while (*buffer && !isspace (*buffer) && len-- > 0 ) *s++ = *buffer++; *s++ = '\0'; break; } } } va_end (ap); return count; } #endif openarena_0.8.8.orig/code/game/g_cmds.c0000644000175000017500000020724111656310264016457 0ustar smcvsmcv/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" #include "../../ui/menudef.h" // for the voice chats /* ================== DeathmatchScoreboardMessage ================== */ void DeathmatchScoreboardMessage( gentity_t *ent ) { char entry[1024]; char string[1400]; int stringlength; int i, j; gclient_t *cl; int numSorted, scoreFlags, accuracy, perfect; // send the latest information on all clients string[0] = 0; stringlength = 0; scoreFlags = 0; numSorted = level.numConnectedClients; for (i=0 ; i < numSorted ; i++) { int ping; cl = &level.clients[level.sortedClients[i]]; if ( cl->pers.connected == CON_CONNECTING ) { ping = -1; } else { //unlagged - true ping //ping = cl->ps.ping < 999 ? cl->ps.ping : 999; ping = cl->pers.realPing < 999 ? cl->pers.realPing : 999; //unlagged - true ping } if( cl->accuracy_shots ) { accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots; } else { accuracy = 0; } perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; if(g_gametype.integer == GT_LMS) { Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, cl->ps.persistant[PERS_IMPRESSIVE_COUNT], cl->ps.persistant[PERS_EXCELLENT_COUNT], cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], cl->ps.persistant[PERS_DEFEND_COUNT], cl->ps.persistant[PERS_ASSIST_COUNT], perfect, cl->ps.persistant[PERS_CAPTURES], cl->pers.livesLeft + (cl->isEliminated?0:1)); } else { Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, cl->ps.persistant[PERS_IMPRESSIVE_COUNT], cl->ps.persistant[PERS_EXCELLENT_COUNT], cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], cl->ps.persistant[PERS_DEFEND_COUNT], cl->ps.persistant[PERS_ASSIST_COUNT], perfect, cl->ps.persistant[PERS_CAPTURES], cl->isEliminated); } j = strlen(entry); if (stringlength + j > 1024) break; strcpy (string + stringlength, entry); stringlength += j; } trap_SendServerCommand( ent-g_entities, va("scores %i %i %i %i%s", i, level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], level.roundStartTime, string ) ); } /* ================== AccMessage ================== */ void AccMessage( gentity_t *ent ) { char entry[1024]; Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i ", ent->client->accuracy[WP_MACHINEGUN][0], ent->client->accuracy[WP_MACHINEGUN][1], ent->client->accuracy[WP_SHOTGUN][0], ent->client->accuracy[WP_SHOTGUN][1], ent->client->accuracy[WP_GRENADE_LAUNCHER][0], ent->client->accuracy[WP_GRENADE_LAUNCHER][1], ent->client->accuracy[WP_ROCKET_LAUNCHER][0], ent->client->accuracy[WP_ROCKET_LAUNCHER][1], ent->client->accuracy[WP_LIGHTNING][0], ent->client->accuracy[WP_LIGHTNING][1], ent->client->accuracy[WP_RAILGUN][0], ent->client->accuracy[WP_RAILGUN][1], ent->client->accuracy[WP_PLASMAGUN][0], ent->client->accuracy[WP_PLASMAGUN][1], ent->client->accuracy[WP_BFG][0], ent->client->accuracy[WP_BFG][1], 0,0, //Hook ent->client->accuracy[WP_NAILGUN][0], ent->client->accuracy[WP_NAILGUN][1], 0,0, ent->client->accuracy[WP_CHAINGUN][0], ent->client->accuracy[WP_CHAINGUN][1] ); trap_SendServerCommand( ent-g_entities, va("accs%s", entry )); } /* ================== DominationPointStatusMessage ================== */ void DominationPointStatusMessage( gentity_t *ent ) { char entry[10]; //Will more likely be 2... in fact cannot be more since we are the server char string[10*(MAX_DOMINATION_POINTS+1)]; int stringlength; int i, j; string[0] = 0; stringlength = 0; for(i = 0;i 10*MAX_DOMINATION_POINTS) break; strcpy (string + stringlength, entry); stringlength += j; } trap_SendServerCommand( ent-g_entities, va("domStatus %i%s", level.domination_points_count, string ) ); } /* ================== EliminationMessage ================== */ void EliminationMessage(gentity_t *ent) { trap_SendServerCommand( ent-g_entities, va("elimination %i %i %i", level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], level.roundStartTime) ); } void RespawnTimeMessage(gentity_t *ent, int time) { trap_SendServerCommand( ent-g_entities, va("respawn %i", time) ); } /* ================== DoubleDominationScoreTime ================== */ void DoubleDominationScoreTimeMessage( gentity_t *ent ) { trap_SendServerCommand( ent-g_entities, va("ddtaken %i", level.timeTaken)); } /* ================== DominationPointNames ================== */ void DominationPointNamesMessage( gentity_t *ent ) { char text[MAX_DOMINATION_POINTS_NAMES*MAX_DOMINATION_POINTS]; int i,j; qboolean nullFound; for(i=0;ihealth <= 0 ) { trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\"")); return qfalse; } return qtrue; } /* ================== ConcatArgs ================== */ char *ConcatArgs( int start ) { int i, c, tlen; static char line[MAX_STRING_CHARS]; int len; char arg[MAX_STRING_CHARS]; len = 0; c = trap_Argc(); for ( i = start ; i < c ; i++ ) { trap_Argv( i, arg, sizeof( arg ) ); tlen = strlen( arg ); if ( len + tlen >= MAX_STRING_CHARS - 1 ) { break; } memcpy( line + len, arg, tlen ); len += tlen; if ( i != c - 1 ) { line[len] = ' '; len++; } } line[len] = 0; return line; } /* ================== ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int ClientNumberFromString( gentity_t *to, char *s ) { gclient_t *cl; int idnum; char cleanName[MAX_STRING_CHARS]; // numeric values are just slot numbers if (s[0] >= '0' && s[0] <= '9') { idnum = atoi( s ); if ( idnum < 0 || idnum >= level.maxclients ) { trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum)); return -1; } cl = &level.clients[idnum]; if ( cl->pers.connected != CON_CONNECTED ) { trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum)); return -1; } return idnum; } // check for a name match for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { if ( cl->pers.connected != CON_CONNECTED ) { continue; } Q_strncpyz(cleanName, cl->pers.netname, sizeof(cleanName)); Q_CleanStr(cleanName); if ( Q_strequal( cleanName, s ) ) { return idnum; } } trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s)); return -1; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (gentity_t *ent) { char *name; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; if ( !CheatsOk( ent ) ) { return; } name = ConcatArgs( 1 ); if Q_strequal(name, "all") give_all = qtrue; else give_all = qfalse; if (give_all || Q_strequal( name, "health")) { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; if (!give_all) return; } if (give_all || Q_strequal(name, "weapons")) { ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE ); if (!give_all) return; } if (give_all || Q_strequal(name, "ammo")) { for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { ent->client->ps.ammo[i] = 999; } if (!give_all) return; } if (give_all || Q_strequal(name, "armor")) { ent->client->ps.stats[STAT_ARMOR] = 200; if (!give_all) return; } if (Q_strequal(name, "excellent")) { ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++; return; } if (Q_strequal(name, "impressive")) { ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; return; } if (Q_strequal(name, "gauntletaward")) { ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; return; } if (Q_strequal(name, "defend")) { ent->client->ps.persistant[PERS_DEFEND_COUNT]++; return; } if (Q_strequal(name, "assist")) { ent->client->ps.persistant[PERS_ASSIST_COUNT]++; return; } // spawn a specific item right on the player if ( !give_all ) { it = BG_FindItem (name); if (!it) { return; } it_ent = G_Spawn(); VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); it_ent->classname = it->classname; G_SpawnItem (it_ent, it); FinishSpawningItem(it_ent ); memset( &trace, 0, sizeof( trace ) ); Touch_Item (it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity( it_ent ); } } } /* ================== Cmd_God_f Sets client to godmode argv(0) god ================== */ void Cmd_God_f (gentity_t *ent) { char *msg; if ( !CheatsOk( ent ) ) { return; } ent->flags ^= FL_GODMODE; if (!(ent->flags & FL_GODMODE) ) msg = "godmode OFF\n"; else msg = "godmode ON\n"; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Notarget_f Sets client to notarget argv(0) notarget ================== */ void Cmd_Notarget_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } ent->flags ^= FL_NOTARGET; if (!(ent->flags & FL_NOTARGET) ) msg = "notarget OFF\n"; else msg = "notarget ON\n"; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Noclip_f argv(0) noclip ================== */ void Cmd_Noclip_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } if ( ent->client->noclip ) { msg = "noclip OFF\n"; } else { msg = "noclip ON\n"; } ent->client->noclip = !ent->client->noclip; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_LevelShot_f( gentity_t *ent ) { if ( !CheatsOk( ent ) ) { return; } // doesn't work in single player if ( g_gametype.integer != 0 ) { trap_SendServerCommand( ent-g_entities, "print \"Must be in g_gametype 0 for levelshot\n\"" ); return; } if(!ent->client->pers.localClient) { trap_SendServerCommand(ent-g_entities, "print \"The levelshot command must be executed by a local client\n\""); return; } BeginIntermission(); trap_SendServerCommand( ent-g_entities, "clientLevelShot" ); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_TeamTask_f( gentity_t *ent ) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); task = atoi( arg ); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f( gentity_t *ent ) { if ( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) || ent->client->isEliminated ) { return; } if (ent->health <= 0) { return; } ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = -999; if(ent->client->lastSentFlying>-1) //If player is in the air because of knockback we give credit to the person who sent it flying player_die (ent, ent, &g_entities[ent->client->lastSentFlying], 100000, MOD_FALLING); else player_die (ent, ent, ent, 100000, MOD_SUICIDE); } /* ================= BroadCastTeamChange Let everyone know about a team change ================= */ void BroadcastTeamChange( gclient_t *client, int oldTeam ) { if ( client->sess.sessionTeam == TEAM_RED ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", client->pers.netname) ); } else if ( client->sess.sessionTeam == TEAM_BLUE ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", client->pers.netname)); } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname)); } else if ( client->sess.sessionTeam == TEAM_FREE ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", client->pers.netname)); } } /* ================= SetTeam KK-OAX Modded this to accept a forced admin change. ================= */ void SetTeam( gentity_t *ent, char *s ) { int team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; int teamLeader; char userinfo[MAX_INFO_STRING]; qboolean force; force = G_admin_permission(ent, ADMF_FORCETEAMCHANGE); // // see what change is requested // client = ent->client; clientNum = client - level.clients; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); specClient = 0; specState = SPECTATOR_NOT; if ( Q_strequal( s, "scoreboard" ) || Q_strequal( s, "score" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if ( Q_strequal( s, "follow1" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if ( Q_strequal( s, "follow2" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; } else if ( Q_strequal( s, "spectator" ) || Q_strequal( s, "s" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if ( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { // if running a team game, assign player to one of the teams specState = SPECTATOR_NOT; if ( Q_strequal( s, "red" ) || Q_strequal( s, "r" ) ) { team = TEAM_RED; } else if ( Q_strequal( s, "blue" ) || Q_strequal( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } if ( !force ) { if ( g_teamForceBalance.integer ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED ); // We allow a spread of two if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Red team has too many players.\n\"" ); return; // ignore the request } if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Blue team has too many players.\n\"" ); return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } } } else { // force them to spectators if there aren't any spots free team = TEAM_FREE; } if ( !force ) { // override decision if limiting the players if ( (g_gametype.integer == GT_TOURNAMENT) && level.numNonSpectatorClients >= 2 ) { team = TEAM_SPECTATOR; } else if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { team = TEAM_SPECTATOR; } } // // decide if we will allow the change // oldTeam = client->sess.sessionTeam; if ( team == oldTeam && team != TEAM_SPECTATOR ) { return; } //KK-OAX Check to make sure the team is not locked from Admin if ( !force ) { if ( team == TEAM_RED && level.RedTeamLocked ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"The Red Team has been locked by the Admin! \n\"" ); return; } if ( team == TEAM_BLUE && level.BlueTeamLocked ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"The Blue Team has been locked by the Admin! \n\"" ); return; } if ( team == TEAM_FREE && level.FFALocked ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"This Deathmatch has been locked by the Admin! \n\"" ); return; } } // // execute the team change // // if the player was dead leave the body if ( client->ps.stats[STAT_HEALTH] <= 0 ) { CopyToBodyQue(ent); } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; if ( oldTeam != TEAM_SPECTATOR ) { int teamscore = -99; //Prevent a team from loosing point because of player leaving team if(g_gametype.integer == GT_TEAM && ent->client->ps.stats[STAT_HEALTH]) teamscore = level.teamScores[ ent->client->sess.sessionTeam ]; // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE); if(teamscore != -99) level.teamScores[ ent->client->sess.sessionTeam ] = teamscore; } if(oldTeam!=TEAM_SPECTATOR) PlayerStore_store(Info_ValueForKey(userinfo,"cl_guid"),client->ps); // they go to the end of the line for tournements if(team == TEAM_SPECTATOR && oldTeam != team) AddTournamentQueue(client); client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; client->sess.teamLeader = qfalse; if ( team == TEAM_RED || team == TEAM_BLUE ) { teamLeader = TeamLeader( team ); // if there is no team leader or the team leader is a bot and this client is not a bot if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) { SetLeader( team, clientNum ); } } // make sure there is a team leader on the team the player came from if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) { CheckTeamLeader( oldTeam ); } BroadcastTeamChange( client, oldTeam ); // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); ClientBegin( clientNum ); } /* ================= StopFollowing If the client being followed leaves the game, or you just want to drop to free floating spectator mode ================= */ void StopFollowing( gentity_t *ent ) { if(g_gametype.integerGT_LMS) { //Shouldn't this already be the case? ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR; ent->client->sess.sessionTeam = TEAM_SPECTATOR; } else { ent->client->ps.stats[STAT_HEALTH] = 0; ent->health = 0; } ent->client->sess.spectatorState = SPECTATOR_FREE; ent->client->ps.pm_flags &= ~PMF_FOLLOW; ent->r.svFlags &= ~SVF_BOT; ent->client->ps.clientNum = ent - g_entities; } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { int oldTeam; char s[MAX_TOKEN_CHARS]; qboolean force; if ( trap_Argc() != 2 ) { oldTeam = ent->client->sess.sessionTeam; switch ( oldTeam ) { case TEAM_BLUE: trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" ); break; case TEAM_RED: trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" ); break; case TEAM_FREE: trap_SendServerCommand( ent-g_entities, "print \"Deathmatch-Playing\n\"" ); break; case TEAM_SPECTATOR: trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" ); break; } return; } force = G_admin_permission(ent, ADMF_FORCETEAMCHANGE); if( !force ) { if ( ent->client->switchTeamTime > level.time ) { trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" ); return; } } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } trap_Argv( 1, s, sizeof( s ) ); SetTeam( ent, s ); ent->client->switchTeamTime = level.time + 5000; } /* ================= Cmd_Follow_f ================= */ void Cmd_Follow_f( gentity_t *ent ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator (or an eliminated player) if ( (level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR) || level.clients[ i ].isEliminated) { return; } if ( (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && g_elimination_lockspectator.integer && ((ent->client->sess.sessionTeam == TEAM_RED && level.clients[ i ].sess.sessionTeam == TEAM_BLUE) || (ent->client->sess.sessionTeam == TEAM_BLUE && level.clients[ i ].sess.sessionTeam == TEAM_RED) ) ) { return; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator //if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) { SetTeam( ent, "spectator" ); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; } /* ================= Cmd_FollowCycle_f KK-OAX Modified to trap arguments. ================= */ void Cmd_FollowCycle_f( gentity_t *ent ) { int clientnum; int original; int count; char args[11]; int dir; if( ent->client->sess.sessionTeam == TEAM_NONE ) { dir = 1; } trap_Argv( 0, args, sizeof( args ) ); if( Q_strequal( args, "followprev" )) { dir = -1; } else if( Q_strequal( args, "follownext" )) { dir = 1; } else { dir = 1; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) { SetTeam( ent, "spectator" ); } if ( dir != 1 && dir != -1 ) { G_Error( "Cmd_FollowCycle_f: bad dir %i", dir ); } clientnum = ent->client->sess.spectatorClient; original = clientnum; count = 0; do { clientnum += dir; count++; if ( clientnum >= level.maxclients ) { clientnum = 0; } if ( clientnum < 0 ) { clientnum = level.maxclients - 1; } if(count>level.maxclients) //We have looked at all clients at least once and found nothing return; //We might end up in an infinite loop here. Stop it! // can only follow connected clients if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) { continue; } // can't follow another spectator if ( (level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR) || level.clients[ clientnum ].isEliminated) { continue; } //Stop players from spectating players on the enemy team in elimination modes. if ( (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) && g_elimination_lockspectator.integer && ((ent->client->sess.sessionTeam == TEAM_RED && level.clients[ clientnum ].sess.sessionTeam == TEAM_BLUE) || (ent->client->sess.sessionTeam == TEAM_BLUE && level.clients[ clientnum ].sess.sessionTeam == TEAM_RED) ) ) { continue; } // this is good, we can use it ent->client->sess.spectatorClient = clientnum; ent->client->sess.spectatorState = SPECTATOR_FOLLOW; return; } while ( clientnum != original ); // leave it where it was } /* ================== G_Say ================== */ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) { if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if ( other->client->pers.connected != CON_CONNECTED ) { return; } if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { return; } if ((ent->r.svFlags & SVF_BOT) && trap_Cvar_VariableValue( "bot_nochat" )>1) return; // no chatting to players in tournements if ( (g_gametype.integer == GT_TOURNAMENT ) && other->client->sess.sessionTeam == TEAM_FREE && ent->client->sess.sessionTeam != TEAM_FREE ) { return; } trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", mode == SAY_TEAM ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message)); } #define EC "\x19" void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { int j; gentity_t *other; int color; char name[64]; // don't let text be too long for malicious reasons char text[MAX_SAY_TEXT]; char location[64]; if ((ent->r.svFlags & SVF_BOT) && trap_Cvar_VariableValue( "bot_nochat" )>1) return; if ( (g_gametype.integer < GT_TEAM || g_ffa_gt == 1) && mode == SAY_TEAM ) { mode = SAY_ALL; } switch ( mode ) { default: case SAY_ALL: G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; case SAY_TEAM: G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); if (Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_CYAN; break; case SAY_TELL: if (target && g_gametype.integer >= GT_TEAM && g_ffa_gt != 1 && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); else Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_MAGENTA; break; } Q_strncpyz( text, chatText, sizeof(text) ); if ( target ) { G_SayTo( ent, target, mode, color, name, text ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "%s%s\n", name, text); } // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_SayTo( ent, other, mode, color, name, text ); } //KK-OAX Admin Command Check from Say/SayTeam line if( g_adminParseSay.integer ) { G_admin_cmd_check ( ent, qtrue ); } } /* ================== Cmd_Say_f KK-OAX Modified this to trap the additional arguments from console. ================== */ static void Cmd_Say_f( gentity_t *ent ){ char *p; char arg[MAX_TOKEN_CHARS]; int mode = SAY_ALL; trap_Argv( 0, arg, sizeof( arg ) ); if( Q_strequal( arg, "say_team" ) ) mode = SAY_TEAM ; // KK-OAX Disabled until PM'ing is added // support parsing /m out of say text since some people have a hard // time figuring out what the console is. /*if( !Q_stricmpn( args, "say /m ", 7 ) || !Q_stricmpn( args, "say_team /m ", 12 ) || !Q_stricmpn( args, "say /mt ", 8 ) || !Q_stricmpn( args, "say_team /mt ", 13 ) ) { Cmd_PrivateMessage_f( ent ); return; } // support parsing /a out of say text for the same reason if( !Q_stricmpn( args, "say /a ", 7 ) || !Q_stricmpn( args, "say_team /a ", 12 ) ) { Cmd_AdminMessage_f( ent ); return; }*/ if( trap_Argc( ) < 2 ) return; p = ConcatArgs( 1 ); G_Say( ent, NULL, mode, p ); } /* ================== Cmd_Tell_f ================== */ static void Cmd_Tell_f( gentity_t *ent ) { int targetNum; gentity_t *target; char *p; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc () < 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } p = ConcatArgs( 2 ); G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Say( ent, ent, SAY_TELL, p ); } } static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) { int color; char *cmd; if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { return; } // no chatting to players in tournements if ( (g_gametype.integer == GT_TOURNAMENT )) { return; } if (mode == SAY_TEAM) { color = COLOR_CYAN; cmd = "vtchat"; } else if (mode == SAY_TELL) { color = COLOR_MAGENTA; cmd = "vtell"; } else { color = COLOR_GREEN; cmd = "vchat"; } trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id)); } void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) { int j; gentity_t *other; if ( (g_gametype.integer < GT_TEAM || g_ffa_gt==1 ) && mode == SAY_TEAM ) { mode = SAY_ALL; } if ( target ) { G_VoiceTo( ent, target, mode, id, voiceonly ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "voice: %s %s\n", ent->client->pers.netname, id); } // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_VoiceTo( ent, other, mode, id, voiceonly ); } } /* ================== Cmd_Voice_f KK-OAX Modified this to trap args. In the original, every call to this function would always set "arg0" to false, and it was never passed along to other functions, so I removed/commented it out. ================== */ static void Cmd_Voice_f( gentity_t *ent ) { char *p; char arg[MAX_TOKEN_CHARS]; int mode = SAY_ALL; qboolean voiceonly = qfalse; trap_Argv( 0, arg, sizeof( arg ) ); if((Q_strequal( arg, "vsay_team" ) ) || Q_strequal( arg, "vosay_team" ) ) mode = SAY_TEAM; if((Q_strequal( arg, "vosay" ) ) || Q_strequal( arg, "vosay_team" ) ) voiceonly = qtrue; //KK-OAX Removed "arg0" since it will always be set to qfalse. if ( trap_Argc () < 2 ) { return; } //KK-OAX This was tricky to figure out, but since none of the original command handlings //set it to "qtrue"... /*if (arg0) { p = ConcatArgs( 0 ); } else {*/ p = ConcatArgs( 1 ); //} G_Voice( ent, NULL, mode, p, voiceonly ); } /* ================== Cmd_VoiceTell_f KK-OAX Modified this to trap args. ================== */ static void Cmd_VoiceTell_f( gentity_t *ent ) { int targetNum; gentity_t *target; char *id; char arg[MAX_TOKEN_CHARS]; qboolean voiceonly = qfalse; if ( trap_Argc () < 2 ) { return; } trap_Argv( 0, arg, sizeof( arg ) ); if( Q_strequal( arg, "votell" ) ) voiceonly = qtrue; trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } id = ConcatArgs( 2 ); G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id ); G_Voice( ent, target, SAY_TELL, id, voiceonly ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, id, voiceonly ); } } /* ================== Cmd_VoiceTaunt_f ================== */ static void Cmd_VoiceTaunt_f( gentity_t *ent ) { gentity_t *who; int i; if (!ent->client) { return; } // insult someone who just killed you if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number) { // i am a dead corpse if (!(ent->enemy->r.svFlags & SVF_BOT)) { G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } ent->enemy = NULL; return; } // insult someone you just killed if (ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number) { who = g_entities + ent->client->lastkilled_client; if (who->client) { // who is the person I just killed if (who->client->lasthurt_mod == MOD_GAUNTLET) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); // and I killed them with a gauntlet } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); } } else { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); // and I killed them with something else } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); } } ent->client->lastkilled_client = -1; return; } } if (g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { // praise a team mate who just got a reward for(i = 0; i < MAX_CLIENTS; i++) { who = g_entities + i; if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) { if (who->client->rewardTime > level.time) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } return; } } } } // just say something G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse ); } static char *gc_orders[] = { "hold your position", "hold this position", "come here", "cover me", "guard location", "search and destroy", "report" }; void Cmd_GameCommand_f( gentity_t *ent ) { int player; int order; char str[MAX_TOKEN_CHARS]; trap_Argv( 1, str, sizeof( str ) ); player = atoi( str ); trap_Argv( 2, str, sizeof( str ) ); order = atoi( str ); if ( player < 0 || player >= MAX_CLIENTS ) { return; } if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) { return; } G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] ); G_Say( ent, ent, SAY_TELL, gc_orders[order] ); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos(ent->r.currentOrigin) ) ); } static const char *gameNames[] = { "Free For All", "Tournament", "Single Player", "Team Deathmatch", "Capture the Flag", "One Flag CTF", "Overload", "Harvester", "Elimination", "CTF Elimination", "Last Man Standing", "Double Domination", "Domination" }; /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { char* c; int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; char buffer[256]; if ( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); return; } if ( level.voteTime ) { trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); return; } if ( ent->client->pers.voteCount >= g_maxvotes.integer ) { trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); // check for command separators in arg2 for( c = arg2; *c; ++c) { switch(*c) { case '\n': case '\r': case ';': trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; break; } } if ( !Q_stricmp( arg1, "map_restart" ) ) { } else if ( !Q_stricmp( arg1, "nextmap" ) ) { } else if ( !Q_stricmp( arg1, "map" ) ) { } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { } else if ( !Q_stricmp( arg1, "kick" ) ) { } else if ( !Q_stricmp( arg1, "clientkick" ) ) { } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) { } else if ( !Q_stricmp( arg1, "timelimit" ) ) { } else if ( !Q_stricmp( arg1, "fraglimit" ) ) { } else if ( !Q_stricmp( arg1, "custom" ) ) { } else if ( !Q_stricmp( arg1, "shuffle" ) ) { } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); //trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit