├── .clang-format ├── .github └── workflows │ ├── build.yml │ └── build_all.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── tasks.json ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── ExportImport.h ├── bg_local.h ├── bg_pmove.h ├── bg_public.h ├── cg_ammo.h ├── cg_cgaz.h ├── cg_cvar.h ├── cg_draw.h ├── cg_entity.h ├── cg_gl.h ├── cg_hud.h ├── cg_jump.h ├── cg_local.h ├── cg_main.h ├── cg_public.h ├── cg_rl.h ├── cg_snap.h ├── cg_syscall.h ├── cg_timer.h ├── cg_utils.h ├── cg_vm.h ├── g_local.h ├── g_public.h ├── nade_tracking.h ├── q_math.h ├── q_shared.h ├── surfaceflags.h └── tr_types.h ├── src ├── CMakeLists.txt ├── bbox.c ├── bbox.h ├── bg_misc.c ├── bg_pmove.c ├── cg_ammo.c ├── cg_cgaz.c ├── cg_consolecmds.c ├── cg_cvar.c ├── cg_draw.c ├── cg_entity.c ├── cg_gl.c ├── cg_hud.c ├── cg_jump.c ├── cg_main.c ├── cg_marks.c ├── cg_rl.c ├── cg_snap.c ├── cg_syscall.c ├── cg_timer.c ├── cg_utils.c ├── cg_view.c ├── cg_vm.c ├── compass.c ├── compass.h ├── defrag.c ├── defrag.h ├── g_missile.c ├── g_weapon.c ├── help.c ├── help.h ├── pitch.c ├── pitch.h ├── q_assert.h ├── q_math.c └── q_shared.c ├── test ├── CMakeLists.txt ├── syscalls.cpp ├── syscalls.hpp ├── syscalls_client_fake.cpp ├── syscalls_client_fake.hpp ├── syscalls_cvar_fake.cpp ├── syscalls_cvar_fake.hpp ├── syscalls_fake.hpp ├── syscalls_mock.cpp └── syscalls_mock.hpp └── version.h.in /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AccessModifierOffset: -2 3 | AlignAfterOpenBracket: AlwaysBreak 4 | AlignConsecutiveAssignments: true 5 | AlignConsecutiveBitFields: true 6 | AlignConsecutiveDeclarations: true 7 | AlignConsecutiveMacros: true 8 | AlignEscapedNewlines: Right 9 | AlignOperands: Align 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: Never 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortEnumsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: None 18 | AllowShortIfStatementsOnASingleLine: true # Always 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortLoopsOnASingleLine: true 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | BraceWrapping: 28 | AfterCaseLabel: true 29 | AfterClass: true 30 | AfterControlStatement: true # Always 31 | AfterEnum: true 32 | AfterFunction: true 33 | AfterNamespace: true 34 | AfterObjCDeclaration: true 35 | AfterStruct: true 36 | AfterUnion: true 37 | AfterExternBlock: true 38 | BeforeCatch: true 39 | BeforeElse: true 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakAfterJavaFieldAnnotations: true 45 | BreakBeforeBinaryOperators: None 46 | BreakBeforeBraces: Custom 47 | BreakBeforeTernaryOperators: true 48 | BreakConstructorInitializers: BeforeComma 49 | BreakInheritanceList: BeforeComma 50 | BreakStringLiterals: false 51 | ColumnLimit: 120 52 | # CommentPragmas: 53 | CompactNamespaces: false 54 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 55 | ConstructorInitializerIndentWidth: 2 56 | ContinuationIndentWidth: 2 57 | Cpp11BracedListStyle: false 58 | DerivePointerAlignment: false 59 | DisableFormat: false 60 | FixNamespaceComments: true 61 | ForEachMacros: ['RANGES_FOR', 'FOREACH', 'BOOST_FOREACH'] 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"' 65 | Priority: 1 66 | - Regex: '^<[[:word:].]+>' 67 | Priority: 3 68 | - Regex: '.+' 69 | Priority: 2 70 | IndentCaseLabels: false 71 | IndentGotoLabels: true 72 | IndentPPDirectives: AfterHash 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: true 75 | KeepEmptyLinesAtTheStartOfBlocks: false 76 | Language: Cpp 77 | MaxEmptyLinesToKeep: 1 78 | NamespaceIndentation: None 79 | ObjCBlockIndentWidth: 2 80 | ObjCSpaceAfterProperty: true 81 | ObjCSpaceBeforeProtocolList: false 82 | PenaltyBreakAssignment: 5 83 | PenaltyBreakBeforeFirstCallParameter: 30 84 | PenaltyBreakComment: 0 85 | PenaltyBreakFirstLessLess: 60 86 | PenaltyBreakString: 15 87 | PenaltyBreakTemplateDeclaration: 0 88 | PenaltyExcessCharacter: 1000000 89 | PenaltyReturnTypeOnItsOwnLine: 1000 90 | PointerAlignment: Left 91 | ReflowComments: true 92 | SortIncludes: true 93 | SortUsingDeclarations: true 94 | SpaceAfterCStyleCast: false 95 | SpaceAfterLogicalNot: false 96 | SpaceAfterTemplateKeyword: true 97 | SpaceBeforeAssignmentOperators: true 98 | SpaceBeforeCpp11BracedList: false 99 | SpaceBeforeCtorInitializerColon: true 100 | SpaceBeforeInheritanceColon: true 101 | SpaceBeforeParens: ControlStatements 102 | SpaceBeforeRangeBasedForLoopColon: true 103 | SpaceInEmptyBlock: true 104 | SpaceInEmptyParentheses: false 105 | SpacesBeforeTrailingComments: 1 106 | SpacesInAngles: false 107 | SpacesInCStyleCastParentheses: false 108 | SpacesInContainerLiterals: true 109 | SpacesInParentheses: false 110 | SpacesInSquareBrackets: false 111 | Standard: Cpp11 # Latest 112 | TabWidth: 2 113 | UseTab: Never 114 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - '**.md' 7 | - '.clang-format' 8 | - '.gitignore' 9 | - '.vscode/**' 10 | push: 11 | branches: 12 | - master 13 | tags: 14 | - v*.*.* 15 | paths-ignore: 16 | - '**.md' 17 | - '.clang-format' 18 | - '.gitignore' 19 | - '.vscode/**' 20 | 21 | workflow_dispatch: 22 | 23 | jobs: 24 | windows: 25 | name: ${{ matrix.config }} Windows ${{ matrix.arch }}-bit (${{ matrix.c }}) 26 | runs-on: windows-latest 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | c: [gcc] 31 | config: [Release] 32 | arch: [32, 64] 33 | include: 34 | - arch: 32 35 | name: cgamex86 36 | msystem: MINGW32 37 | prefix: mingw-w64-i686 38 | - arch: 64 39 | name: cgamex86_64 40 | msystem: MINGW64 41 | prefix: mingw-w64-x86_64 42 | - c: gcc 43 | cxx: g++ 44 | 45 | defaults: 46 | run: 47 | shell: msys2 {0} 48 | 49 | steps: 50 | - uses: msys2/setup-msys2@v2 51 | with: 52 | install: ${{ matrix.prefix }}-cmake ${{ matrix.prefix }}-ninja ${{ matrix.prefix }}-${{ matrix.c }} 53 | msystem: ${{ matrix.msystem }} 54 | path-type: minimal 55 | release: false 56 | update: false 57 | 58 | - uses: actions/checkout@v3 59 | with: 60 | fetch-depth: 0 # fetch annotated tags for proper versioning 61 | submodules: recursive 62 | 63 | - name: Configure 64 | run: cmake 65 | -S . 66 | -B build 67 | -G Ninja 68 | -DBINARY_NAME=${{ matrix.name }} 69 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 70 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m${{ matrix.arch }}" 71 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m${{ matrix.arch }}" 72 | -DCMAKE_C_COMPILER=${{ matrix.c }} 73 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 74 | 75 | - name: Build 76 | run: cmake --build build 77 | 78 | - name: Test 79 | working-directory: build 80 | run: ctest --no-tests=error --output-on-failure 81 | 82 | - name: Install 83 | run: cmake --install build --prefix bin --strip 84 | 85 | - uses: actions/upload-artifact@v3 86 | with: 87 | name: artifact 88 | path: bin/*.dll 89 | if-no-files-found: error 90 | retention-days: 5 91 | 92 | ubuntu: 93 | name: ${{ matrix.config }} Ubuntu ${{ matrix.arch }}-bit (${{ matrix.c }}) 94 | runs-on: ubuntu-latest 95 | strategy: 96 | fail-fast: false 97 | matrix: 98 | c: [gcc] 99 | config: [Release] 100 | arch: [32, 64] 101 | include: 102 | - arch: 32 103 | name: cgamei386 104 | - arch: 64 105 | name: cgamex86_64 106 | - c: gcc 107 | cxx: g++ 108 | 109 | steps: 110 | - name: Install tools 111 | run: | 112 | sudo apt-get -qq update 113 | if [ ${{ matrix.arch }} -eq 32 ]; then 114 | sudo apt-get -y install gcc-multilib g++-multilib 115 | fi 116 | sudo apt-get -y install ninja-build 117 | 118 | - uses: actions/checkout@v3 119 | with: 120 | fetch-depth: 0 # fetch annotated tags for proper versioning 121 | submodules: recursive 122 | 123 | - name: Configure 124 | run: cmake 125 | -S . 126 | -B build 127 | -G Ninja 128 | -DBINARY_NAME=${{ matrix.name }} 129 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 130 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m${{ matrix.arch }}" 131 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m${{ matrix.arch }}" 132 | -DCMAKE_C_COMPILER=${{ matrix.c }} 133 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 134 | 135 | - name: Build 136 | run: cmake --build build 137 | 138 | - name: Test 139 | working-directory: build 140 | run: ctest --no-tests=error --output-on-failure 141 | 142 | - name: Install 143 | run: cmake --install build --prefix bin --strip 144 | 145 | - uses: actions/upload-artifact@v3 146 | with: 147 | name: artifact 148 | path: bin/*.so 149 | if-no-files-found: error 150 | retention-days: 5 151 | 152 | macos: 153 | name: ${{ matrix.config }} macOS 64-bit (${{ matrix.c }}) 154 | runs-on: macos-latest 155 | strategy: 156 | fail-fast: false 157 | matrix: 158 | c: [clang] 159 | config: [Release] 160 | include: 161 | - c: clang 162 | cxx: clang++ 163 | 164 | steps: 165 | - name: Install tools 166 | run: brew install ninja 167 | 168 | - uses: actions/checkout@v3 169 | with: 170 | fetch-depth: 0 # fetch annotated tags for proper versioning 171 | submodules: recursive 172 | 173 | - name: Configure 174 | run: cmake 175 | -S . 176 | -B build 177 | -G Ninja 178 | -DBINARY_NAME=cgamex86_64 179 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 180 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m64" 181 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m64" 182 | -DCMAKE_C_COMPILER=${{ matrix.c }} 183 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 184 | 185 | - name: Build 186 | run: cmake --build build 187 | 188 | - name: Test 189 | working-directory: build 190 | run: ctest --no-tests=error --output-on-failure 191 | 192 | - name: Install 193 | run: cmake --install build --prefix bin --strip 194 | 195 | - uses: actions/upload-artifact@v3 196 | with: 197 | name: artifact 198 | path: bin/*.dylib 199 | if-no-files-found: error 200 | retention-days: 5 201 | 202 | pre-release: 203 | name: Pre-release 204 | if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') 205 | needs: [windows, ubuntu, macos] 206 | runs-on: ubuntu-latest 207 | 208 | steps: 209 | - uses: actions/checkout@v3 210 | with: 211 | fetch-depth: 0 # fetch annotated tags for proper versioning 212 | submodules: recursive 213 | 214 | - uses: actions/download-artifact@v3 215 | with: 216 | name: artifact 217 | path: bin/ 218 | 219 | - uses: actions/github-script@v6 220 | id: script 221 | with: 222 | script: | 223 | // delete 'latest' pre-release 224 | try { 225 | const prerelease = await github.rest.repos.getReleaseByTag({ 226 | owner: context.repo.owner, 227 | repo: context.repo.repo, 228 | tag: "latest", 229 | }); 230 | console.log(`Found latest pre-release`) 231 | await github.rest.repos.deleteRelease({ 232 | owner: context.repo.owner, 233 | repo: context.repo.repo, 234 | release_id: prerelease.data.id, 235 | }); 236 | console.log(`Deleted latest pre-release`); 237 | } catch (e) { 238 | console.log(`Failed deleting latest pre-release: ${e.message}`); 239 | } 240 | 241 | // create/update 'latest' tag 242 | try { 243 | await github.rest.git.createRef({ 244 | owner: context.repo.owner, 245 | repo: context.repo.repo, 246 | ref: "refs/tags/latest", 247 | sha: context.sha, 248 | }); 249 | console.log(`Created new \`latest\` tag`); 250 | } catch (e) { 251 | await github.rest.git.updateRef({ 252 | owner: context.repo.owner, 253 | repo: context.repo.repo, 254 | ref: "tags/latest", 255 | sha: context.sha, 256 | }); 257 | console.log(`Updated \`latest\` tag`); 258 | } 259 | 260 | // compare commits 261 | let commits = []; 262 | try { 263 | const latest_release = await github.rest.repos.getLatestRelease({ 264 | owner: context.repo.owner, 265 | repo: context.repo.repo, 266 | }); 267 | console.log(`Found latest release \`${latest_release.data.tag_name}\``); 268 | const r_commits = await github.rest.repos.compareCommits({ 269 | owner: context.repo.owner, 270 | repo: context.repo.repo, 271 | base: latest_release.data.tag_name, 272 | head: "latest", 273 | }); 274 | commits = r_commits.data.commits; 275 | console.log(`Found ${commits.length} commits comparing \`${latest_release.data.tag_name}\`...\`latest\``); 276 | } catch (e) { 277 | console.log(`No commits found`); 278 | } 279 | 280 | // find associated pull requests 281 | const find_pull_requests = async (commit) => { 282 | let pull_requests = []; 283 | try { 284 | const r_pull_requests = await github.rest.repos.listPullRequestsAssociatedWithCommit({ 285 | owner: context.repo.owner, 286 | repo: context.repo.repo, 287 | commit_sha: commit.sha, 288 | }); 289 | if (r_pull_requests.data.length > 0) { 290 | console.log(`Found ${r_pull_requests.data.length} pull requests for commit ${commit.sha}`); 291 | for (const pull_request of r_pull_requests.data) { 292 | pull_requests.push(`[#${pull_request.number}](${pull_request.html_url})`); 293 | } 294 | } 295 | } catch (e) { 296 | core.setFailed(`Failed retrieving associated pull requests for commit ${commit.sha}: ${e.message}`); 297 | } 298 | return pull_requests; 299 | }; 300 | const pull_requests = await Promise.all(commits.map(commit => find_pull_requests(commit))); 301 | 302 | // prepare body 303 | let body = `Please see [CHANGELOG](CHANGELOG.md#Unreleased) for all notable changes.\n`; 304 | if (commits.length > 0) { 305 | body += `### Commits\n`; 306 | for (const [i, commit] of commits.entries()) { 307 | const message = commit.commit.message.slice(0, commit.commit.message.indexOf("\n")); 308 | body += `- [\`${commit.sha.slice(0, 7)}\`](${commit.html_url}) ${message}`; 309 | if (pull_requests[i].length > 0) { 310 | body += ` (${pull_requests[i].join(", ")})`; 311 | } 312 | body += `\n`; 313 | } 314 | } 315 | core.setOutput("body", body); 316 | 317 | - uses: softprops/action-gh-release@v1 318 | with: 319 | body: ${{ steps.script.outputs.body }} 320 | draft: false 321 | prerelease: false 322 | files: | 323 | bin/*.dll 324 | bin/*.so 325 | bin/*.dylib 326 | name: Latest release 327 | tag_name: latest 328 | token: ${{ secrets.GITHUB_TOKEN }} 329 | -------------------------------------------------------------------------------- /.github/workflows/build_all.yml: -------------------------------------------------------------------------------- 1 | name: build_all 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | windows_gcc: 7 | name: ${{ matrix.config }} Windows ${{ matrix.arch }}-bit (${{ matrix.c }}) 8 | runs-on: windows-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | c: [gcc, clang] 13 | config: [Debug, Release] 14 | arch: [32, 64] 15 | include: 16 | - arch: 32 17 | name: cgamex86 18 | msystem: MINGW32 19 | prefix: mingw-w64-i686 20 | - arch: 64 21 | name: cgamex86_64 22 | msystem: MINGW64 23 | prefix: mingw-w64-x86_64 24 | - c: gcc 25 | cxx: g++ 26 | - c: clang 27 | cxx: clang++ 28 | 29 | defaults: 30 | run: 31 | shell: msys2 {0} 32 | 33 | steps: 34 | - uses: msys2/setup-msys2@v2 35 | with: 36 | install: ${{ matrix.prefix }}-cmake ${{ matrix.prefix }}-ninja ${{ matrix.prefix }}-${{ matrix.c }} 37 | msystem: ${{ matrix.msystem }} 38 | path-type: minimal 39 | release: false 40 | update: false 41 | 42 | - uses: actions/checkout@v3 43 | with: 44 | submodules: recursive 45 | 46 | - name: Configure 47 | run: cmake 48 | -S . 49 | -B build 50 | -G Ninja 51 | -DBINARY_NAME=${{ matrix.name }} 52 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 53 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m${{ matrix.arch }}" 54 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m${{ matrix.arch }}" 55 | -DCMAKE_C_COMPILER=${{ matrix.c }} 56 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 57 | 58 | - name: Build 59 | run: cmake --build build 60 | 61 | - name: Test 62 | working-directory: build 63 | run: ctest --no-tests=error --output-on-failure 64 | 65 | - name: Install 66 | if: matrix.c == 'gcc' && matrix.config == 'Release' 67 | run: cmake --install build --prefix bin --strip 68 | 69 | - uses: actions/upload-artifact@v3 70 | if: matrix.c == 'gcc' && matrix.config == 'Release' 71 | with: 72 | name: artifact 73 | path: bin/*.dll 74 | if-no-files-found: error 75 | retention-days: 5 76 | 77 | windows_msvc: 78 | name: ${{ matrix.config }} Windows ${{ matrix.arch }}-bit (MSVC) 79 | runs-on: windows-latest 80 | strategy: 81 | fail-fast: false 82 | matrix: 83 | config: [Debug, Release] 84 | arch: [32, 64] 85 | include: 86 | - arch: 32 87 | name: cgamex86 88 | A: Win32 89 | - arch: 64 90 | name: cgamex86_64 91 | A: x64 92 | 93 | steps: 94 | - uses: actions/checkout@v3 95 | with: 96 | submodules: recursive 97 | 98 | - name: Configure 99 | run: cmake 100 | -S . 101 | -B build 102 | -G "Visual Studio 17 2022" 103 | -A ${{ matrix.A }} 104 | -DBINARY_NAME=${{ matrix.name }} 105 | -DCMAKE_C_FLAGS_DEBUG="${CMAKE_C_FLAGS_DEBUG} /MTd" 106 | -DCMAKE_CXX_FLAGS_DEBUG="${CMAKE_CXX_FLAGS_DEBUG} /MTd" 107 | -DCMAKE_C_FLAGS_RELEASE="${CMAKE_C_FLAGS_RELEASE} /MT" 108 | -DCMAKE_CXX_FLAGS_RELEASE="${CMAKE_CXX_FLAGS_RELEASE} /MT" 109 | -DCMAKE_C_COMPILER=cl 110 | -DCMAKE_CXX_COMPILER=cl 111 | 112 | - name: Build 113 | run: cmake --build build --config ${{ matrix.config }} 114 | 115 | - name: Test 116 | working-directory: build 117 | run: ctest --build-config ${{ matrix.config }} --no-tests=error --output-on-failure 118 | 119 | ubuntu: 120 | name: ${{ matrix.config }} Ubuntu ${{ matrix.arch }}-bit (${{ matrix.c }}) 121 | runs-on: ubuntu-latest 122 | strategy: 123 | fail-fast: false 124 | matrix: 125 | c: [gcc, clang] 126 | config: [Debug, Release] 127 | arch: [32, 64] 128 | include: 129 | - arch: 32 130 | name: cgamei386 131 | - arch: 64 132 | name: cgamex86_64 133 | - c: gcc 134 | cxx: g++ 135 | - c: clang 136 | cxx: clang++ 137 | 138 | steps: 139 | - name: Install tools 140 | run: | 141 | sudo apt-get -qq update 142 | if [ ${{ matrix.arch }} -eq 32 ]; then 143 | sudo apt-get -y install gcc-multilib g++-multilib 144 | fi 145 | sudo apt-get -y install ninja-build 146 | 147 | - uses: actions/checkout@v3 148 | with: 149 | submodules: recursive 150 | 151 | - name: Configure 152 | run: cmake 153 | -S . 154 | -B build 155 | -G Ninja 156 | -DBINARY_NAME=${{ matrix.name }} 157 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 158 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m${{ matrix.arch }}" 159 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m${{ matrix.arch }}" 160 | -DCMAKE_C_COMPILER=${{ matrix.c }} 161 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 162 | 163 | - name: Build 164 | run: cmake --build build 165 | 166 | - name: Test 167 | working-directory: build 168 | run: ctest --no-tests=error --output-on-failure 169 | 170 | - name: Install 171 | if: matrix.c == 'gcc' && matrix.config == 'Release' 172 | run: cmake --install build --prefix bin --strip 173 | 174 | - uses: actions/upload-artifact@v3 175 | if: matrix.c == 'gcc' && matrix.config == 'Release' 176 | with: 177 | name: artifact 178 | path: bin/*.so 179 | if-no-files-found: error 180 | retention-days: 5 181 | 182 | macos: 183 | name: ${{ matrix.config }} macOS 64-bit (${{ matrix.c }}) 184 | runs-on: macos-latest 185 | strategy: 186 | fail-fast: false 187 | matrix: 188 | # Apple redirects gcc to clang => explicitly use gcc-12 189 | # see https://github.com/actions/virtual-environments/blob/main/images/macos/macos-12-Readme.md#language-and-runtime 190 | c: [gcc-12, clang] 191 | config: [Debug, Release] 192 | include: 193 | - c: gcc-12 194 | cxx: g++-12 195 | - c: clang 196 | cxx: clang++ 197 | 198 | steps: 199 | - name: Install tools 200 | run: brew install ninja 201 | 202 | - uses: actions/checkout@v3 203 | with: 204 | submodules: recursive 205 | 206 | - name: Configure 207 | run: cmake 208 | -S . 209 | -B build 210 | -G Ninja 211 | -DBINARY_NAME=cgamex86_64 212 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 213 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m64" 214 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m64" 215 | -DCMAKE_C_COMPILER=${{ matrix.c }} 216 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} 217 | 218 | - name: Build 219 | run: cmake --build build 220 | 221 | - name: Test 222 | working-directory: build 223 | run: ctest --no-tests=error --output-on-failure 224 | 225 | - name: Install 226 | if: matrix.c == 'clang' && matrix.config == 'Release' 227 | run: cmake --install build --prefix bin --strip 228 | 229 | - uses: actions/upload-artifact@v3 230 | if: matrix.c == 'clang' && matrix.config == 'Release' 231 | with: 232 | name: artifact 233 | path: bin/*.dylib 234 | if-no-files-found: error 235 | retention-days: 5 236 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore thumbnails created by windows 2 | Thumbs.db 3 | #Ignore files build by Visual Studio 4 | *.obj 5 | *.exe 6 | *.pdb 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *_i.c 12 | *_p.c 13 | *.ncb 14 | *.suo 15 | *.tlb 16 | *.tlh 17 | *.bak 18 | *.cache 19 | *.ilk 20 | *.log 21 | [Bb]in 22 | [Bb]uild/ 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "googletest"] 2 | path = googletest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch UnitTest", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/Debug/Linux_64-bit/test/UnitTest", 12 | "stopAtEntry": false, 13 | "cwd": "${workspaceFolder}", 14 | "environment": [], 15 | "externalConsole": false, 16 | "MIMode": "gdb", 17 | "setupCommands": [ 18 | { 19 | "description": "Enable pretty-printing for gdb", 20 | "text": "-enable-pretty-printing", 21 | "ignoreFailures": true 22 | } 23 | ] 24 | }, 25 | { 26 | "name": "(gdb) Launch Engine", 27 | "type": "cppdbg", 28 | "request": "launch", 29 | "program": "${env:HOME}/Quake3e/quake3e.x64", 30 | "args": [ 31 | "+set", 32 | "fs_game", 33 | "defrag" 34 | ], 35 | "stopAtEntry": false, 36 | "cwd": "${workspaceFolder}", 37 | "environment": [], 38 | "externalConsole": false, 39 | "MIMode": "gdb", 40 | "setupCommands": [ 41 | { 42 | "description": "Enable pretty-printing for gdb", 43 | "text": "-enable-pretty-printing", 44 | "ignoreFailures": true 45 | } 46 | ] 47 | }, 48 | { 49 | "name": "(gdb) Attach", 50 | "type": "cppdbg", 51 | "request": "attach", 52 | "program": "${env:HOME}/Quake3e/quake3e.x64", 53 | "processId": "${command:pickProcess}", 54 | "MIMode": "gdb", 55 | "setupCommands": [ 56 | { 57 | "description": "Enable pretty-printing for gdb", 58 | "text": "-enable-pretty-printing", 59 | "ignoreFailures": true 60 | } 61 | ] 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "clean all", 8 | "type": "shell", 9 | "command": "rm", 10 | "args": [ 11 | "-rf", 12 | "${workspaceFolder}/build", 13 | "${workspaceFolder}/bin" 14 | ], 15 | "group": "build", 16 | "problemMatcher": [] 17 | }, 18 | { 19 | "label": "configure Debug Linux 32-bit", 20 | "type": "shell", 21 | "command": "cmake", 22 | "args": [ 23 | "-S", 24 | "${workspaceFolder}", 25 | "-B", 26 | "${workspaceFolder}/build/Debug/Linux_32-bit", 27 | "-G", 28 | "Ninja", 29 | "-DBINARY_NAME=cgamei386", 30 | "-DCMAKE_BUILD_TYPE=Debug", 31 | "-DCMAKE_C_COMPILER=gcc-9", 32 | "-DCMAKE_CXX_COMPILER=g++-9", 33 | "-DCMAKE_C_FLAGS=\"${CMAKE_C_FLAGS} -m32\"", 34 | "-DCMAKE_CXX_FLAGS=\"${CMAKE_CXX_FLAGS} -m32\"" 35 | ] 36 | }, 37 | { 38 | "label": "configure Debug Linux 64-bit", 39 | "type": "shell", 40 | "command": "cmake", 41 | "args": [ 42 | "-S", 43 | "${workspaceFolder}", 44 | "-B", 45 | "${workspaceFolder}/build/Debug/Linux_64-bit", 46 | "-G", 47 | "Ninja", 48 | "-DBINARY_NAME=cgamex86_64", 49 | "-DCMAKE_BUILD_TYPE=Debug", 50 | "-DCMAKE_C_COMPILER=gcc-9", 51 | "-DCMAKE_CXX_COMPILER=g++-9", 52 | "-DCMAKE_C_FLAGS=\"${CMAKE_C_FLAGS} -m64\"", 53 | "-DCMAKE_CXX_FLAGS=\"${CMAKE_CXX_FLAGS} -m64\"" 54 | ] 55 | }, 56 | { 57 | "label": "configure Release Linux 32-bit", 58 | "type": "shell", 59 | "command": "cmake", 60 | "args": [ 61 | "-S", 62 | "${workspaceFolder}", 63 | "-B", 64 | "${workspaceFolder}/build/Release/Linux_32-bit", 65 | "-G", 66 | "Ninja", 67 | "-DBINARY_NAME=cgamei386", 68 | "-DCMAKE_BUILD_TYPE=Release", 69 | "-DCMAKE_C_COMPILER=gcc-9", 70 | "-DCMAKE_CXX_COMPILER=g++-9", 71 | "-DCMAKE_C_FLAGS=\"${CMAKE_C_FLAGS} -m32\"", 72 | "-DCMAKE_CXX_FLAGS=\"${CMAKE_CXX_FLAGS} -m32\"" 73 | ] 74 | }, 75 | { 76 | "label": "configure Release Linux 64-bit", 77 | "type": "shell", 78 | "command": "cmake", 79 | "args": [ 80 | "-S", 81 | "${workspaceFolder}", 82 | "-B", 83 | "${workspaceFolder}/build/Release/Linux_64-bit", 84 | "-G", 85 | "Ninja", 86 | "-DBINARY_NAME=cgamex86_64", 87 | "-DCMAKE_BUILD_TYPE=Release", 88 | "-DCMAKE_C_COMPILER=gcc-9", 89 | "-DCMAKE_CXX_COMPILER=g++-9", 90 | "-DCMAKE_C_FLAGS=\"${CMAKE_C_FLAGS} -m64\"", 91 | "-DCMAKE_CXX_FLAGS=\"${CMAKE_CXX_FLAGS} -m64\"" 92 | ] 93 | }, 94 | { 95 | "label": "build Debug Linux 32-bit", 96 | "type": "shell", 97 | "command": "cmake", 98 | "args": [ 99 | "--build", 100 | "build/Debug/Linux_32-bit" 101 | ], 102 | "problemMatcher": { 103 | "base": "$gcc", 104 | "fileLocation": [ 105 | "relative", 106 | "${workspaceFolder}/build/Debug/Linux_32-bit" 107 | ] 108 | }, 109 | "group": "build", 110 | "dependsOn": [ 111 | "configure Debug Linux 32-bit" 112 | ] 113 | }, 114 | { 115 | "label": "build Debug Linux 64-bit", 116 | "type": "shell", 117 | "command": "cmake", 118 | "args": [ 119 | "--build", 120 | "build/Debug/Linux_64-bit" 121 | ], 122 | "problemMatcher": { 123 | "base": "$gcc", 124 | "fileLocation": [ 125 | "relative", 126 | "${workspaceFolder}/build/Debug/Linux_64-bit" 127 | ] 128 | }, 129 | "group": "build", 130 | "dependsOn": [ 131 | "configure Debug Linux 64-bit" 132 | ] 133 | }, 134 | { 135 | "label": "build Release Linux 32-bit", 136 | "type": "shell", 137 | "command": "cmake", 138 | "args": [ 139 | "--build", 140 | "build/Release/Linux_32-bit" 141 | ], 142 | "problemMatcher": { 143 | "base": "$gcc", 144 | "fileLocation": [ 145 | "relative", 146 | "${workspaceFolder}/build/Release/Linux_32-bit" 147 | ] 148 | }, 149 | "group": "build", 150 | "dependsOn": [ 151 | "configure Release Linux 32-bit" 152 | ] 153 | }, 154 | { 155 | "label": "build Release Linux 64-bit", 156 | "type": "shell", 157 | "command": "cmake", 158 | "args": [ 159 | "--build", 160 | "build/Release/Linux_64-bit" 161 | ], 162 | "problemMatcher": { 163 | "base": "$gcc", 164 | "fileLocation": [ 165 | "relative", 166 | "${workspaceFolder}/build/Release/Linux_64-bit" 167 | ] 168 | }, 169 | "group": "build", 170 | "dependsOn": [ 171 | "configure Release Linux 64-bit" 172 | ] 173 | }, 174 | { 175 | "label": "install Debug Linux 32-bit", 176 | "type": "shell", 177 | "command": "cmake", 178 | "args": [ 179 | "--install", 180 | "build/Debug/Linux_32-bit", 181 | "--prefix", 182 | "${env:HOME}/Quake3e/defrag" 183 | ], 184 | "problemMatcher": [], 185 | "group": "build", 186 | "dependsOn": [ 187 | "build Debug Linux 32-bit" 188 | ] 189 | }, 190 | { 191 | "label": "install Debug Linux 64-bit", 192 | "type": "shell", 193 | "command": "cmake", 194 | "args": [ 195 | "--install", 196 | "build/Debug/Linux_64-bit", 197 | "--prefix", 198 | "${env:HOME}/Quake3e/defrag" 199 | ], 200 | "problemMatcher": [], 201 | "group": { 202 | "kind": "build", 203 | "isDefault": true 204 | }, 205 | "dependsOn": [ 206 | "build Debug Linux 64-bit" 207 | ] 208 | }, 209 | { 210 | "label": "install Release Linux 32-bit", 211 | "type": "shell", 212 | "command": "cmake", 213 | "args": [ 214 | "--install", 215 | "build/Release/Linux_32-bit", 216 | "--prefix", 217 | "bin", 218 | "--strip" 219 | ], 220 | "problemMatcher": [], 221 | "group": "build", 222 | "dependsOn": [ 223 | "build Release Linux 32-bit" 224 | ] 225 | }, 226 | { 227 | "label": "install Release Linux 64-bit", 228 | "type": "shell", 229 | "command": "cmake", 230 | "args": [ 231 | "--install", 232 | "build/Release/Linux_64-bit", 233 | "--prefix", 234 | "bin", 235 | "--strip" 236 | ], 237 | "problemMatcher": [], 238 | "group": "build", 239 | "dependsOn": [ 240 | "build Release Linux 64-bit" 241 | ] 242 | }, 243 | { 244 | "label": "build Debug All", 245 | "group": "build", 246 | "dependsOn": [ 247 | "build Debug Linux 32-bit", 248 | "build Debug Linux 64-bit", 249 | ], 250 | "problemMatcher": [] 251 | }, 252 | { 253 | "label": "install Release All", 254 | "group": "build", 255 | "dependsOn": [ 256 | "install Release Linux 32-bit", 257 | "install Release Linux 64-bit", 258 | ], 259 | "problemMatcher": [] 260 | } 261 | ] 262 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ### Added 9 | - Add support for defrag versions 1.91.29, 1.91.30 and 1.91.31. 10 | - Pitch hud which marks one or more pitch angles, e.g. `mdd_pitch 71 66`. 11 | - Bounding box `mdd_bbox`. It uses shader `bbox_nocull` and draws the full bbox with `1` and only the bottom with `2`. 12 | 13 | ### Changed 14 | - Don't draw hud when using freecam, `cg_draw2D 0` or `+scores`. 15 | 16 | ### Fixed 17 | - Correctly position Snap-HUD when roll is not zero. ([#8](https://github.com/Jelvan1/cgame_proxymod/pull/8)) 18 | 19 | ## [1.4.0] - 2021-03-16 20 | ### Added 21 | - Add support for defrag versions 1.91.24, 1.91.25, 1.91.26 and 1.91.27. 22 | - New cvar `mdd_snap_min_speed`. Like cvar `mdd_cgaz_min_speed`, a minimum velocity is required to show the hud. 23 | 24 | ### Changed 25 | - Don't show the compass and the timer hud by default. 26 | - Make compass, CampingGaz-HUD and Snap-HUD thinner by default. 27 | - Rename `mdd_cgaz_speed` cvar to `mdd_cgaz_min_speed`. 28 | - Rename `mdd_snap_speed` cvar to `mdd_snap_scale` and change the default accordingly. 29 | 30 | ## [1.3.1] - 2020-05-05 31 | ### Changed 32 | - Only support defrag version 1.91.26. 33 | 34 | ## [1.3.0] - 2020-04-19 35 | ### Added 36 | - Compass which is more accurate/flexible than `df_hud_cgaz`. 37 | - New cvar `mdd_projection`. Determines how angles will be projected on the screen. This affects the compass, CampingGaz-HUD and Snap-HUD. 38 | 39 | ## [1.2.0] - 2020-04-13 40 | ### Added 41 | - New command `mdd_help`. Provides information about cvars. 42 | 43 | ### Changed 44 | - Invert meaning of `mdd_ammo`'s 2nd bit from the right. 45 | - Extract the *trueness* bits from the `mdd_cgaz` cvar into `mdd_cgaz_trueness`. 46 | - Rename `mdd_snap` cvar to `mdd_snap_trueness`. 47 | - Rename `mdd_snap1` cvar to `mdd_snap`. 48 | - Rename all `mdd_snap1_` cvars to `mdd_snap_`. 49 | 50 | ### Deprecated 51 | - Wrong snapzones are shown when the *ground* bit is enabled in `mdd_snap_trueness`. Friction is not taken into account and will not happen in the near future. 52 | 53 | ### Removed 54 | - Don't display second snaphud and remove all its `mdd_snap2` cvars. 55 | 56 | ## [1.1.3] - 2020-02-10 57 | ### Changed 58 | - Draw all vanilla/defrag 2D hud elements (e.g. crosshair) on top of CampingGaz-HUD and Snap-HUD. 59 | 60 | ### Fixed 61 | - Correctly display infinite ammo (i.e. -1). 62 | - Draw fired grenade paths even after switching weapons. 63 | 64 | ## [1.1.2] - 2019-11-23 65 | ### Changed 66 | - Rename `mdd_time` into `mdd_timer` to align more with the other commands of the timer hud. 67 | 68 | ## [1.1.1] - 2019-11-22 69 | ### Changed 70 | - Don't bloat q3config.cfg and improve cross-platform config compatibility by only archiving cvars that have non-default values. 71 | 72 | ## [1.1.0] - 2019-11-22 73 | ### Added 74 | - New cvar `mdd_fov`. When different from `0`, CampingGaz-HUD and Snap-HUD will use this fov instead of `cg_fov`. 75 | 76 | ### Removed 77 | - Bounding box trigger display since engines support this now (`scr_triggers_draw 1` and `scr_clips_draw 1`). 78 | 79 | ## [1.0.0] - 2019-11-21 80 | ### Added 81 | - Ammo and jump hud extended from [q3df](https://github.com/q3df/cgame_proxymod). 82 | - Grenade, rocket, timer, trigger huds extended from [krsh732](https://github.com/krsh732/cgame_proxymod). 83 | - Option to filter other player sounds extended from [krsh732](https://github.com/krsh732/cgame_proxymod). 84 | - Initial CampingGaz-HUD which is more accurate/flexible than `df_hud_cgaz`. 85 | - Initial Snap-HUD which is more accurate/flexible than `scr_hud_snap`. 86 | 87 | [Unreleased]: ../../compare/v1.4.0...HEAD 88 | [1.4.0]: ../../compare/v1.3.1...v1.4.0 89 | [1.3.1]: ../../compare/v1.3.0...v1.3.1 90 | [1.3.0]: ../../compare/v1.2.0...v1.3.0 91 | [1.2.0]: ../../compare/v1.1.3...v1.2.0 92 | [1.1.3]: ../../compare/v1.1.2...v1.1.3 93 | [1.1.2]: ../../compare/v1.1.1...v1.1.2 94 | [1.1.1]: ../../compare/v1.1.0...v1.1.1 95 | [1.1.0]: ../../compare/v1.0.0...v1.1.0 96 | [1.0.0]: ../../releases/tag/v1.0.0 97 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(cgame_proxymod 4 | VERSION 1.4.0 # .. 5 | ) 6 | 7 | set(BINARY_NAME cgame CACHE STRING "Name of the cgame_proxymod binary") 8 | 9 | # set cgame version: 10 | # release X.X.X 11 | # pre-release X.X.X+HEAD_SHA1 12 | # others 0.0.0 or HEAD_SHA1 13 | find_package(Git QUIET) 14 | if(GIT_FOUND) 15 | # re-configure if the Git index file is touched 16 | set_property(DIRECTORY APPEND 17 | PROPERTY CMAKE_CONFIGURE_DEPENDS 18 | ${CMAKE_SOURCE_DIR}/.git/index) 19 | 20 | execute_process(COMMAND 21 | ${GIT_EXECUTABLE} rev-parse --short HEAD 22 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 23 | OUTPUT_VARIABLE HEAD_SHA1 24 | ERROR_QUIET 25 | OUTPUT_STRIP_TRAILING_WHITESPACE) 26 | 27 | set(TAG_NAME v${PROJECT_VERSION}) 28 | execute_process(COMMAND 29 | ${GIT_EXECUTABLE} rev-list --abbrev-commit -n 1 tags/${TAG_NAME} 30 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 31 | OUTPUT_VARIABLE TAG_SHA1 32 | ERROR_QUIET 33 | OUTPUT_STRIP_TRAILING_WHITESPACE) 34 | 35 | if(NOT TAG_SHA1) 36 | message(WARNING "No annotated tag ${TAG_NAME} found.") 37 | set(CGAME_VERSION ${HEAD_SHA1}) 38 | else() 39 | execute_process(COMMAND 40 | ${GIT_EXECUTABLE} merge-base --is-ancestor ${TAG_SHA1} ${HEAD_SHA1} 41 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 42 | RESULT_VARIABLE ANCESTOR_EXIT_CODE 43 | ERROR_QUIET) 44 | 45 | if(NOT ANCESTOR_EXIT_CODE EQUAL "0") 46 | message(WARNING "Annotated tag ${TAG_NAME} [${TAG_SHA1}] is not an ancestor of HEAD [${HEAD_SHA1}].") 47 | set(CGAME_VERSION ${HEAD_SHA1}) 48 | elseif(NOT HEAD_SHA1 STREQUAL TAG_SHA1) 49 | string(CONCAT CGAME_VERSION ${PROJECT_VERSION}+${HEAD_SHA1}) # pre-release 50 | else() 51 | set(CGAME_VERSION ${PROJECT_VERSION}) # release 52 | endif() 53 | endif() 54 | else() 55 | set(CGAME_VERSION "0.0.0") 56 | endif() 57 | 58 | configure_file(version.h.in version.h) 59 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 60 | 61 | if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR 62 | CMAKE_CXX_COMPILE_ID STREQUAL "GNU") 63 | add_compile_options(-fdiagnostics-color=always) 64 | elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR 65 | CMAKE_CXX_COMPILE_ID STREQUAL "Clang") 66 | add_compile_options(-fcolor-diagnostics) 67 | endif() 68 | 69 | add_subdirectory(src) 70 | 71 | option(ENABLE_TESTING "Enable the tests" ON) 72 | if(ENABLE_TESTING) 73 | enable_testing() 74 | set(CMAKE_CXX_STANDARD 17) # set C++ standard for googletest 75 | set(INSTALL_GTEST OFF CACHE BOOL "") # disable installation of googletest 76 | add_subdirectory(googletest) 77 | add_subdirectory(test) 78 | endif() 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mDd client Proxymod 2 | 3 | [![build](../../workflows/build/badge.svg)](../../actions?query=workflow%3Abuild) 4 | 5 | This proxymod helps players to train their abilities in the Quake III Arena modification DeFRaG. It adds hud elements to the standard defrag hud. 6 | 7 | ## Installation 8 | Quick and easy: 9 | 1. Find the right *cgame* platform specific binary in the [latest release](../../releases/tag/latest). To know which binary to choose, run `\version` in your engine's console. This will tell you *Windows vs Linux vs macOS* and *32-bit vs 64-bit*. 10 | 11 | | | **Windows** | **Linux** | **macOS** | 12 | | :--------: | :---------: | :-------: | :-------: | 13 | | **32-bit** | [cgamex86.dll](../../releases/download/latest/cgamex86.dll) | [cgamei386.so](../../releases/download/latest/cgamei386.so) | 14 | | **64-bit** | [cgamex86_64.dll](../../releases/download/latest/cgamex86_64.dll) | [cgamex86_64.so](../../releases/download/latest/cgamex86_64.so) | [cgamex86_64.dylib](../../releases/download/latest/cgamex86_64.dylib) | 15 | 16 | 2. Download and copy this binary into the *defrag* folder of your Quake III Arena directory. 17 | 3. Put `seta vm_cgame 0` in your config file (e.g. *defrag/autoexec.cfg*). 18 | 19 | You've correctly installed the proxymod if you load your favorite map and you see the following colored text in the console: `[mDd] cgame-proxy X.X.X`. 20 | 21 | ## Changelog 22 | Please see [CHANGELOG](CHANGELOG.md) for notable changes between [releases](../../releases). 23 | 24 | ## Configuration 25 | All commands and cvars start with `mdd_` and can be listed by typing this prefix in the console followed by pressing the *tab* key. Information about those cvars can be requested with `mdd_help `. 26 | 27 | Default values can be shown by typing the cvar into the console followed by pressing *enter* key. There are a few things worth noting: 28 | * Some cvars end with `_w`, `_xh` or even `_xywh`. Here `w` stands for width, `h` for height and `x` and `y` for the coordinates. Multiple values are separated by spaces. 29 | E.g., `mdd_ammo_graph_xywh 610 100 24 24`, `mdd_cgaz_yh 180 12`. 30 | * Some cvars have binary-literals as default values. They start with `0b` followed by a sequence of `1`'s and `0`'s. 31 | E.g., `mdd_ammo 0b0011`, `mdd_cgaz_trueness 0b110`. 32 | Note that it's not necessary to have the same total number of `1`'s and `0`'s as there are *different items* for these cvars, or even use the binary representation altogether. You can still use the good old decimal equivalent (or the octal and hexadecimal representation to impress your friends). 33 | 34 | ## Examples 35 | ### `mdd_ammo 0b1101` 36 | * draw the ammo hud 37 | * show the ammo even if you don't have the weapon 38 | * use gun icons instead of ammo icons 39 | * use 3D models instead of 2D models 40 | 41 | So if you want to use 2D models, simply use `mdd_ammo 0b0101`. 42 | 43 | ### `mdd_cgaz_trueness 0b110` 44 | * don't show true jump/crouch zones, ignore their influence 45 | * show true CPM air control zones 46 | * show true ground zones 47 | 48 | ## Building 49 | This C/C++ proxymod project uses CMake to control the building process. Both GitHub Actions and Visual Studio Code build tasks are available and basically do the following steps: 50 | 1. Generate input files for a native build system. 51 | To generate standard Makefiles for a 32-bit release build on Windows with gcc, do: 52 | ``` 53 | $ cmake \ 54 | -S \ 55 | -B \ 56 | -DBINARY_NAME=cgamex86 \ 57 | -DCMAKE_BUILD_TYPE=Release \ 58 | -DCMAKE_C_COMPILER=gcc \ 59 | -DCMAKE_CXX_COMPILER=g++ \ 60 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m32" \ 61 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m32" 62 | ``` 63 | To generate Ninja files for a 64-bit debug build on Linux with clang, do: 64 | ``` 65 | $ cmake \ 66 | -S \ 67 | -B \ 68 | -G Ninja \ 69 | -DBINARY_NAME=cgamex86_64 \ 70 | -DCMAKE_BUILD_TYPE=Debug \ 71 | -DCMAKE_C_COMPILER=clang \ 72 | -DCMAKE_CXX_COMPILER=clang++ \ 73 | -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -m64" \ 74 | -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -m64" 75 | ``` 76 | Note that the binary name for a 32-bit Windows build is `cgamex86`, while `cgamei386` on Linux. The binary name for a 64-bit build, however, is `cgamex86_64` on both Windows and Linux. 77 | 2. Build the source code. 78 | ``` 79 | $ cmake --build 80 | ``` 81 | 3. Optionally, you can strip and directly install the new binary into the *defrag* folder of your Quake III Arena directory by specifying an install prefix. 82 | ``` 83 | $ cmake --install --prefix /defrag --strip 84 | ``` 85 | 4. Profit. 86 | -------------------------------------------------------------------------------- /include/ExportImport.h: -------------------------------------------------------------------------------- 1 | #ifndef IMPORTEXPORT_H 2 | #define IMPORTEXPORT_H 3 | 4 | #if defined(BUILDING_SHARED) 5 | /* Building shared library. */ 6 | # define EXPORTIMPORT EXPORT 7 | #elif defined(USING_SHARED) 8 | /* Using shared library. */ 9 | # define EXPORTIMPORT IMPORT 10 | #else 11 | /* Building static library. */ 12 | # define EXPORTIMPORT 13 | #endif 14 | 15 | #if defined(_MSC_VER) 16 | # define EXPORT __declspec(dllexport) 17 | # define IMPORT __declspec(dllimport) 18 | #elif defined(__GNUC__) || defined(__clang__) 19 | # define EXPORT __attribute__((visibility("default"))) 20 | # define IMPORT 21 | #else 22 | # error Unsupported compiler. 23 | #endif 24 | 25 | #if defined(_MSC_VER) 26 | # define QDECL __cdecl 27 | #elif defined(__clang__) 28 | # define QDECL __attribute__((cdecl)) 29 | #elif defined(__GNUC__) 30 | # if defined(__x86_64__) 31 | # define QDECL // warning: 'cdecl' attribute ignored [-Wattributes] 32 | # elif defined(__i386__) 33 | # define QDECL __attribute__((cdecl)) 34 | # else 35 | # error Unsupported architecture. 36 | # endif 37 | #else 38 | # error Unsupported compiler. 39 | #endif 40 | 41 | #endif // IMPORTEXPORT_H 42 | -------------------------------------------------------------------------------- /include/bg_local.h: -------------------------------------------------------------------------------- 1 | #ifndef BG_LOCAL_H 2 | #define BG_LOCAL_H 3 | 4 | #include "q_shared.h" 5 | 6 | #define MIN_WALK_NORMAL .7f // can't walk on very steep slopes 7 | 8 | #define STEPSIZE 18 9 | 10 | #define JUMP_VELOCITY 270 11 | 12 | #define OVERCLIP 1.001f 13 | 14 | #define pm_frametime .008f 15 | 16 | // movement parameters 17 | #define pm_stopspeed 100.f 18 | #define pm_duckScale .25f 19 | #define pm_swimScale .50f 20 | 21 | #define pm_accelerate 10.f 22 | #define pm_airaccelerate 1.f 23 | #define pm_slickaccelerate 1.f 24 | #define pm_wateraccelerate 4.f 25 | #define pm_flyaccelerate 8.f 26 | 27 | #define cpm_accelerate 15.f 28 | #define cpm_slickaccelerate 15.f 29 | 30 | #define cpm_airstopaccelerate 2.5f 31 | #define cpm_airstrafeaccelerate 70.f 32 | #define cpm_airwishspeed 30.f 33 | 34 | #define pm_friction 6.f 35 | #define pm_waterfriction 1.f 36 | #define pm_flightfriction 3.f 37 | #define pm_spectatorfriction 5.f 38 | 39 | // all of the locals will be zeroed before each 40 | // pmove, just to make damn sure we don't have 41 | // any differences when running on client or server 42 | typedef struct 43 | { 44 | vec3_t forward, right, up; 45 | float frametime; 46 | 47 | int32_t msec; 48 | 49 | qboolean walking; 50 | qboolean groundPlane; 51 | trace_t groundTrace; 52 | 53 | float impactSpeed; 54 | 55 | vec3_t previous_origin; 56 | vec3_t previous_velocity; 57 | int32_t previous_waterlevel; 58 | } pml_t; 59 | 60 | #endif // BG_LOCAL_H 61 | -------------------------------------------------------------------------------- /include/bg_pmove.h: -------------------------------------------------------------------------------- 1 | #ifndef BG_PMOVE_H 2 | #define BG_PMOVE_H 3 | 4 | #include "bg_local.h" 5 | #include "bg_public.h" 6 | 7 | float PM_CmdScale(playerState_t const* pm_ps, usercmd_t const* cmd); 8 | float PM_AltCmdScale(playerState_t const* pm_ps, usercmd_t const* cmd); 9 | 10 | qboolean PM_CheckJump(pmove_t* pm, playerState_t* pm_ps, pml_t* pml); 11 | 12 | void PM_GroundTrace(pmove_t* pm, playerState_t* pm_ps, pml_t* pml); 13 | 14 | void PM_SetWaterLevel(pmove_t* pm, playerState_t* pm_ps); 15 | 16 | void PM_CheckDuck(pmove_t* pm, playerState_t* pm_ps); 17 | 18 | #endif // BG_PMOVE_H 19 | -------------------------------------------------------------------------------- /include/bg_public.h: -------------------------------------------------------------------------------- 1 | #ifndef BG_PUBLIC_H 2 | #define BG_PUBLIC_H 3 | 4 | #include "q_shared.h" 5 | 6 | #define DEFAULT_GRAVITY 800 7 | 8 | #define MAX_ITEMS 256 9 | 10 | #define LIGHTNING_RANGE 768 11 | 12 | #define MINS_Z -24 13 | #define DEFAULT_VIEWHEIGHT 26 14 | #define CROUCH_VIEWHEIGHT 12 15 | #define DEAD_VIEWHEIGHT -16 16 | 17 | // 18 | // config strings are a general means of communicating variable length strings 19 | // from the server to all connected clients. 20 | // 21 | 22 | // CS_SERVERINFO and CS_SYSTEMINFO are defined in q_shared.h 23 | #define CS_LEVEL_START_TIME 21 // so the timer only shows the current level 24 | 25 | #define CS_ITEMS 27 // string of 0's and 1's that tell which items are present 26 | 27 | #define CS_MODELS 32 28 | 29 | /* 30 | =================================================================================== 31 | 32 | PMOVE MODULE 33 | 34 | The pmove code takes a player_state_t and a usercmd_t and generates a new player_state_t 35 | and some other output data. Used for local prediction on the client game and true 36 | movement on the server game. 37 | =================================================================================== 38 | */ 39 | 40 | typedef enum 41 | { 42 | PM_NORMAL, // can accelerate and turn 43 | PM_NOCLIP, // noclip movement 44 | PM_SPECTATOR, // still run into walls 45 | PM_DEAD, // no acceleration or turning, but free falling 46 | PM_FREEZE, // stuck in place with no control 47 | PM_INTERMISSION, // no movement or status bar 48 | PM_SPINTERMISSION // no movement or status bar 49 | } pmtype_t; 50 | 51 | typedef enum 52 | { 53 | WEAPON_READY, 54 | WEAPON_RAISING, 55 | WEAPON_DROPPING, 56 | WEAPON_FIRING 57 | } weaponstate_t; 58 | 59 | // pmove->pm_flags 60 | #define PMF_DUCKED 1 61 | #define PMF_JUMP_HELD 2 62 | #define PMF_BACKWARDS_JUMP 8 // go into backwards land 63 | #define PMF_BACKWARDS_RUN 16 // coast down to backwards run 64 | #define PMF_TIME_LAND 32 // pm_time is time before rejump 65 | #define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time 66 | #define PMF_TIME_WATERJUMP 256 // pm_time is waterjump 67 | #define PMF_RESPAWNED 512 // clear after attack and jump buttons come up 68 | #define PMF_USE_ITEM_HELD 1024 69 | #define PMF_GRAPPLE_PULL 2048 // pull towards grapple location 70 | #define PMF_FOLLOW 4096 // spectate following another player 71 | #define PMF_SCOREBOARD 8192 // spectate as a scoreboard 72 | #define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size 73 | 74 | #define PMF_PROMODE 32768 // is CPM physic 75 | 76 | #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_KNOCKBACK) 77 | 78 | #define MAXTOUCH 32 79 | typedef struct 80 | { 81 | // state (in / out) 82 | int32_t ps; // playerState_t* 83 | 84 | // command (in) 85 | usercmd_t cmd; 86 | int32_t tracemask; // collide against these types of surfaces 87 | qboolean killWallBug; 88 | int32_t debugLevel; // if set, diagnostic output will be printed 89 | qboolean noFootsteps; // if the game is setup for no footsteps by the server 90 | qboolean gauntletHit; // true if a gauntlet attack would actually hit something 91 | 92 | int32_t framecount; 93 | 94 | // results (out) 95 | int32_t numtouch; 96 | int32_t touchents[MAXTOUCH]; 97 | 98 | vec3_t mins, maxs; // bounding box size 99 | 100 | int32_t watertype; 101 | int32_t waterlevel; 102 | 103 | float xyspeed; 104 | 105 | // for fixed msec Pmove 106 | int32_t pmove_fixed; 107 | int32_t pmove_msec; 108 | 109 | // callbacks to test the world 110 | // these will be different functions during game and cgame 111 | int32_t trace; // void (*trace)(trace_t* results, vec3_t const start, vec3_t const mins, vec3_t const maxs, 112 | // vec3_t const end, int32_t passEntityNum, int32_t contentMask); 113 | int32_t pointcontents; // int32_t (*pointcontents)(vec3_t const point, int32_t passEntityNum); 114 | } pmove_t; 115 | 116 | //=================================================================================== 117 | 118 | // player_state->stats[] indexes 119 | // NOTE: may not have more than 16 120 | typedef enum 121 | { 122 | STAT_HEALTH, 123 | STAT_HOLDABLE_ITEM, 124 | #ifdef MISSIONPACK 125 | STAT_PERSISTANT_POWERUP, 126 | #endif 127 | STAT_WEAPONS, // 16 bit fields 128 | STAT_ARMOR, 129 | STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) 130 | STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) 131 | STAT_MAX_HEALTH // health / armor limit, changeable by handicap 132 | } statIndex_t; 133 | 134 | // entityState_t->eFlags 135 | #define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD 136 | #define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes 137 | #define EF_AWARD_EXCELLENT 0x00000008 // draw an excellent sprite 138 | #define EF_PLAYER_EVENT 0x00000010 139 | #define EF_BOUNCE 0x00000010 // for missiles 140 | #define EF_BOUNCE_HALF 0x00000020 // for missiles 141 | #define EF_AWARD_GAUNTLET 0x00000040 // draw a gauntlet sprite 142 | #define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) 143 | #define EF_FIRING 0x00000100 // for lightning gun 144 | #define EF_KAMIKAZE 0x00000200 145 | #define EF_MOVER_STOP 0x00000400 // will push otherwise 146 | #define EF_AWARD_CAP 0x00000800 // draw the capture sprite 147 | #define EF_TALK 0x00001000 // draw a talk balloon 148 | #define EF_CONNECTION 0x00002000 // draw a connection trouble sprite 149 | #define EF_VOTED 0x00004000 // already cast a vote 150 | #define EF_AWARD_IMPRESSIVE 0x00008000 // draw an impressive sprite 151 | #define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite 152 | #define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite 153 | #define EF_AWARD_DENIED 0x00040000 // denied 154 | #define EF_TEAMVOTED 0x00080000 // already cast a team vote 155 | 156 | // NOTE: may not have more than 16 157 | typedef enum 158 | { 159 | PW_NONE, 160 | 161 | PW_QUAD, 162 | PW_BATTLESUIT, 163 | PW_HASTE, 164 | PW_INVIS, 165 | PW_REGEN, 166 | PW_FLIGHT, 167 | 168 | PW_REDFLAG, 169 | PW_BLUEFLAG, 170 | PW_NEUTRALFLAG, 171 | 172 | PW_SCOUT, 173 | PW_GUARD, 174 | PW_DOUBLER, 175 | PW_AMMOREGEN, 176 | PW_INVULNERABILITY, 177 | 178 | PW_NUM_POWERUPS 179 | } powerup_t; 180 | 181 | typedef enum 182 | { 183 | WP_NONE, 184 | 185 | WP_GAUNTLET, 186 | WP_MACHINEGUN, 187 | WP_SHOTGUN, 188 | WP_GRENADE_LAUNCHER, 189 | WP_ROCKET_LAUNCHER, 190 | WP_LIGHTNING, 191 | WP_RAILGUN, 192 | WP_PLASMAGUN, 193 | WP_BFG, 194 | WP_GRAPPLING_HOOK, 195 | #ifdef MISSIONPACK 196 | WP_NAILGUN, 197 | WP_PROX_LAUNCHER, 198 | WP_CHAINGUN, 199 | #endif 200 | 201 | WP_NUM_WEAPONS 202 | } weapon_t; 203 | 204 | typedef struct animation_s 205 | { 206 | int32_t firstFrame; 207 | int32_t numFrames; 208 | int32_t loopFrames; // 0 to numFrames 209 | int32_t frameLerp; // msec between frames 210 | int32_t initialLerp; // msec to get to first frame 211 | int32_t reversed; // true if animation is reversed 212 | int32_t flipflop; // true if animation should flipflop back to base 213 | } animation_t; 214 | 215 | // content masks 216 | #define MASK_ALL (-1) 217 | #define MASK_SOLID (CONTENTS_SOLID) 218 | #define MASK_PLAYERSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_BODY) 219 | #define MASK_DEADSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP) 220 | #define MASK_WATER (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME) 221 | #define MASK_OPAQUE (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA) 222 | #define MASK_SHOT (CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE) 223 | 224 | // 225 | // entityState_t->eType 226 | // 227 | typedef enum 228 | { 229 | ET_GENERAL, 230 | ET_PLAYER, 231 | ET_ITEM, 232 | ET_MISSILE, 233 | ET_MOVER, 234 | ET_BREAKABLE, 235 | ET_BEAM, 236 | ET_PORTAL, 237 | ET_SPEAKER, 238 | ET_PUSH_TRIGGER, 239 | ET_TELEPORT_TRIGGER, 240 | ET_PHYSICS_TRIGGER, 241 | ET_INVISIBLE, 242 | ET_GRAPPLE, // grapple hooked on wall 243 | ET_SPAWNPOINT, 244 | ET_SPECTATOR, 245 | ET_TEAM, 246 | 247 | ET_EVENTS // any of the EV_* events can be added freestanding 248 | // by setting eType to ET_EVENTS + eventNum 249 | // this avoids having to set eFlags and eventNum 250 | } entityType_t; 251 | 252 | void BG_EvaluateTrajectory(trajectory_t const* tr, int32_t atTime, vec3_t result); 253 | void BG_EvaluateTrajectoryDelta(trajectory_t const* tr, int32_t atTime, vec3_t result); 254 | 255 | void BG_PlayerStateToEntityState(playerState_t const* pm_ps, entityState_t* s, qboolean snap); 256 | 257 | #endif // BG_PUBLIC_H 258 | -------------------------------------------------------------------------------- /include/cg_ammo.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_AMMO_H 2 | #define CG_AMMO_H 3 | 4 | void init_ammo(void); 5 | 6 | void update_ammo(void); 7 | 8 | void draw_ammo(void); 9 | 10 | #endif // CG_AMMO_H 11 | -------------------------------------------------------------------------------- /include/cg_cgaz.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_CGAZ_H 2 | #define CG_CGAZ_H 3 | 4 | void init_cgaz(void); 5 | 6 | void update_cgaz(void); 7 | 8 | void draw_cgaz(void); 9 | 10 | #endif // CG_CGAZ_H 11 | -------------------------------------------------------------------------------- /include/cg_cvar.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #ifndef CG_CVAR_H 22 | #define CG_CVAR_H 23 | 24 | #include "q_shared.h" 25 | 26 | #include 27 | 28 | typedef struct 29 | { 30 | vmCvar_t* vmCvar; 31 | char* cvarName; 32 | char* defaultString; 33 | int32_t cvarFlags; 34 | } cvarTable_t; 35 | 36 | char const* ParseVec(char const* data, vec_t* vec, uint8_t size); 37 | char const* ParseVec4(char const* data, vec4_t* vec, uint8_t size); 38 | 39 | int32_t cvar_getInteger(char const* var_name); 40 | float cvar_getValue(char const* var_name); 41 | 42 | void init_cvars(cvarTable_t const* cvars, size_t size); 43 | void update_cvars(cvarTable_t const* cvars, size_t size); 44 | 45 | #endif // CG_CVAR_H 46 | -------------------------------------------------------------------------------- /include/cg_draw.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #ifndef CG_DRAW_H 22 | #define CG_DRAW_H 23 | 24 | #include "q_shared.h" 25 | 26 | void CG_AdjustFrom640(float* x, float* y, float* w, float* h); 27 | void CG_FillRect(float x, float y, float w, float h, vec4_t const color); 28 | void CG_DrawSides(float x, float y, float w, float h, float size); 29 | void CG_DrawTopBottom(float x, float y, float w, float h, float size); 30 | void CG_DrawRect(float x, float y, float w, float h, float size, vec4_t const color); 31 | void CG_DrawPic(float x, float y, float w, float h, qhandle_t hShader); 32 | 33 | void CG_DrawChar(float x, float y, float w, float h, uint8_t ch); 34 | void CG_DrawText( 35 | float x, 36 | float y, 37 | float sizePx, 38 | char const* string, 39 | vec4_t const color, 40 | qboolean alignRight, 41 | qboolean shadow); 42 | 43 | void CG_Draw3DModel( 44 | float x, 45 | float y, 46 | float w, 47 | float h, 48 | qhandle_t model, 49 | qhandle_t skin, 50 | vec3_t const origin, 51 | vec3_t const angles); 52 | 53 | void CG_FillAnglePitch(float start, float end, float pitch, float x, float w, vec4_t const color); 54 | void CG_DrawLinePitch(float angle, float pitch, float x, float w, float h, vec4_t const color); 55 | 56 | void CG_FillAngleYaw(float start, float end, float yaw, float y, float h, vec4_t const color); 57 | void CG_DrawLineYaw(float angle, float yaw, float y, float w, float h, vec4_t const color); 58 | void CG_DrawCharYaw(float angle, float yaw, float y, float w, float h, uint8_t ch, vec4_t const color); 59 | 60 | #endif // CG_DRAW_H 61 | -------------------------------------------------------------------------------- /include/cg_entity.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_ENTITY_H 2 | #define CG_ENTITY_H 3 | 4 | #include 5 | 6 | void init_entityStates(void); 7 | 8 | void update_entityStates(void); 9 | 10 | int8_t should_filter_sound(int entity_num, int8_t is_loop); 11 | 12 | #endif // CG_ENTITY_H 13 | -------------------------------------------------------------------------------- /include/cg_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_GL_H 2 | #define CG_GL_H 3 | 4 | void init_gl(void); 5 | 6 | void update_gl(void); 7 | 8 | void draw_gl(void); 9 | 10 | #endif // CG_GL_H 11 | -------------------------------------------------------------------------------- /include/cg_hud.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #ifndef CG_HUD_H 22 | #define CG_HUD_H 23 | 24 | #include 25 | 26 | void init_hud(void); 27 | 28 | void del_hud(void); 29 | 30 | void update_hud(void); 31 | 32 | void draw_hud(void); 33 | 34 | #endif // CG_HUD_H 35 | -------------------------------------------------------------------------------- /include/cg_jump.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_JUMP_H 2 | #define CG_JUMP_H 3 | 4 | void init_jump(void); 5 | 6 | void update_jump(void); 7 | 8 | void draw_jump(void); 9 | 10 | #endif // CG_JUMP_H 11 | -------------------------------------------------------------------------------- /include/cg_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #ifndef CG_MAIN_H 22 | #define CG_MAIN_H 23 | 24 | #include "ExportImport.h" 25 | 26 | #include 27 | 28 | typedef enum 29 | { 30 | CG_INIT, 31 | // void CG_Init( int32_t serverMessageNum, int32_t serverCommandSequence, int32_t clientNum ) 32 | // called when the level loads or when the renderer is restarted 33 | // all media should be registered at this time 34 | // cgame will display loading status by calling SCR_Update, which 35 | // will call CG_DrawInformation during the loading process 36 | // reliableCommandSequence will be 0 on fresh loads, but higher for 37 | // demos, tourney restarts, or vid_restarts 38 | 39 | CG_SHUTDOWN, 40 | // void (*CG_Shutdown)( void ); 41 | // opportunity to flush and close any open files 42 | 43 | CG_CONSOLE_COMMAND, 44 | // qboolean (*CG_ConsoleCommand)( void ); 45 | // a console command has been issued locally that is not recognized by the 46 | // main game system. 47 | // use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the 48 | // command is not known to the game 49 | 50 | CG_DRAW_ACTIVE_FRAME, 51 | // void (*CG_DrawActiveFrame)( int32_t serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); 52 | // Generates and draws a game scene and status information at the given time. 53 | // If demoPlayback is set, local movement prediction will not be enabled 54 | 55 | CG_CROSSHAIR_PLAYER, 56 | // int32_t (*CG_CrosshairPlayer)( void ); 57 | 58 | CG_LAST_ATTACKER, 59 | // int32_t (*CG_LastAttacker)( void ); 60 | 61 | CG_KEY_EVENT, 62 | // void (*CG_KeyEvent)( int32_t key, qboolean down ); 63 | 64 | CG_MOUSE_EVENT, 65 | // void (*CG_MouseEvent)( int32_t dx, int32_t dy ); 66 | 67 | CG_EVENT_HANDLING 68 | // void (*CG_EventHandling)(int32_t type); 69 | } cgameExport_t; 70 | 71 | EXPORTIMPORT intptr_t vmMain( 72 | int32_t cmd, 73 | int32_t arg0, 74 | int32_t arg1, 75 | int32_t arg2, 76 | int32_t arg3, 77 | int32_t arg4, 78 | int32_t arg5, 79 | int32_t arg6, 80 | int32_t arg7, 81 | int32_t arg8, 82 | int32_t arg9, 83 | int32_t arg10, 84 | int32_t arg11); 85 | 86 | #endif // CG_MAIN_H 87 | -------------------------------------------------------------------------------- /include/cg_public.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_PUBLIC_H 2 | #define CG_PUBLIC_H 3 | 4 | #include "q_shared.h" 5 | 6 | #define MAX_ENTITIES_IN_SNAPSHOT 256 7 | 8 | // snapshots are a view of the server at a given time 9 | 10 | // Snapshots are generated at regular time intervals by the server, 11 | // but they may not be sent if a client's rate level is exceeded, or 12 | // they may be dropped by the network. 13 | typedef struct 14 | { 15 | int32_t snapFlags; // SNAPFLAG_RATE_DELAYED, etc 16 | int32_t ping; 17 | 18 | int32_t serverTime; // server time the message is valid for (in msec) 19 | 20 | byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits 21 | 22 | playerState_t ps; // complete information about the current player at this time 23 | 24 | int32_t numEntities; // all of the entities that need to be presented 25 | entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot 26 | 27 | int32_t numServerCommands; // text based server commands to execute when this 28 | int32_t serverCommandSequence; // snapshot becomes current 29 | } snapshot_t; 30 | 31 | /* 32 | ================================================================== 33 | 34 | functions imported from the main executable 35 | 36 | ================================================================== 37 | */ 38 | 39 | typedef enum 40 | { 41 | CG_PRINT, 42 | CG_ERROR, 43 | CG_MILLISECONDS, 44 | CG_CVAR_REGISTER, 45 | CG_CVAR_UPDATE, 46 | CG_CVAR_SET, 47 | CG_CVAR_VARIABLESTRINGBUFFER, 48 | CG_ARGC, 49 | CG_ARGV, 50 | CG_ARGS, 51 | CG_FS_FOPENFILE, 52 | CG_FS_READ, 53 | CG_FS_WRITE, 54 | CG_FS_FCLOSEFILE, 55 | CG_SENDCONSOLECOMMAND, 56 | CG_ADDCOMMAND, 57 | CG_SENDCLIENTCOMMAND, 58 | CG_UPDATESCREEN, 59 | CG_CM_LOADMAP, 60 | CG_CM_NUMINLINEMODELS, 61 | CG_CM_INLINEMODEL, 62 | CG_CM_LOADMODEL, 63 | CG_CM_TEMPBOXMODEL, 64 | CG_CM_POINTCONTENTS, 65 | CG_CM_TRANSFORMEDPOINTCONTENTS, 66 | CG_CM_BOXTRACE, 67 | CG_CM_TRANSFORMEDBOXTRACE, 68 | CG_CM_MARKFRAGMENTS, 69 | CG_S_STARTSOUND, 70 | CG_S_STARTLOCALSOUND, 71 | CG_S_CLEARLOOPINGSOUNDS, 72 | CG_S_ADDLOOPINGSOUND, 73 | CG_S_UPDATEENTITYPOSITION, 74 | CG_S_RESPATIALIZE, 75 | CG_S_REGISTERSOUND, 76 | CG_S_STARTBACKGROUNDTRACK, 77 | CG_R_LOADWORLDMAP, 78 | CG_R_REGISTERMODEL, 79 | CG_R_REGISTERSKIN, 80 | CG_R_REGISTERSHADER, 81 | CG_R_CLEARSCENE, 82 | CG_R_ADDREFENTITYTOSCENE, 83 | CG_R_ADDPOLYTOSCENE, 84 | CG_R_ADDLIGHTTOSCENE, 85 | CG_R_RENDERSCENE, 86 | CG_R_SETCOLOR, 87 | CG_R_DRAWSTRETCHPIC, 88 | CG_R_MODELBOUNDS, 89 | CG_R_LERPTAG, 90 | CG_GETGLCONFIG, 91 | CG_GETGAMESTATE, 92 | CG_GETCURRENTSNAPSHOTNUMBER, 93 | CG_GETSNAPSHOT, 94 | CG_GETSERVERCOMMAND, 95 | CG_GETCURRENTCMDNUMBER, 96 | CG_GETUSERCMD, 97 | CG_SETUSERCMDVALUE, 98 | CG_R_REGISTERSHADERNOMIP, 99 | CG_MEMORY_REMAINING, 100 | CG_R_REGISTERFONT, 101 | CG_KEY_ISDOWN, 102 | CG_KEY_GETCATCHER, 103 | CG_KEY_SETCATCHER, 104 | CG_KEY_GETKEY, 105 | CG_PC_ADD_GLOBAL_DEFINE, 106 | CG_PC_LOAD_SOURCE, 107 | CG_PC_FREE_SOURCE, 108 | CG_PC_READ_TOKEN, 109 | CG_PC_SOURCE_FILE_AND_LINE, 110 | CG_S_STOPBACKGROUNDTRACK, 111 | CG_REAL_TIME, 112 | CG_SNAPVECTOR, 113 | CG_REMOVECOMMAND, 114 | CG_R_LIGHTFORPOINT, 115 | CG_CIN_PLAYCINEMATIC, 116 | CG_CIN_STOPCINEMATIC, 117 | CG_CIN_RUNCINEMATIC, 118 | CG_CIN_DRAWCINEMATIC, 119 | CG_CIN_SETEXTENTS, 120 | CG_R_REMAP_SHADER, 121 | CG_S_ADDREALLOOPINGSOUND, 122 | CG_S_STOPLOOPINGSOUND, 123 | 124 | CG_CM_TEMPCAPSULEMODEL, 125 | CG_CM_CAPSULETRACE, 126 | CG_CM_TRANSFORMEDCAPSULETRACE, 127 | CG_R_ADDADDITIVELIGHTTOSCENE, 128 | CG_GET_ENTITY_TOKEN, 129 | CG_R_ADDPOLYSTOSCENE, 130 | CG_R_INPVS, 131 | // 1.32 132 | CG_FS_SEEK, 133 | 134 | /* 135 | CG_LOADCAMERA, 136 | CG_STARTCAMERA, 137 | CG_GETCAMERAINFO, 138 | */ 139 | 140 | CG_MEMSET = 100, 141 | CG_MEMCPY, 142 | CG_STRNCPY, 143 | CG_SIN, 144 | CG_COS, 145 | CG_ATAN2, 146 | CG_SQRT, 147 | CG_FLOOR, 148 | CG_CEIL, 149 | CG_TESTPRINTINT, 150 | CG_TESTPRINTFLOAT, 151 | CG_ACOS 152 | } cgameImport_t; 153 | 154 | #endif // CG_PUBLIC_H 155 | -------------------------------------------------------------------------------- /include/cg_rl.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_RL_H 2 | #define CG_RL_H 3 | 4 | void init_rl(void); 5 | 6 | void update_rl(void); 7 | 8 | void draw_rl(void); 9 | 10 | #endif // CG_RL_H 11 | -------------------------------------------------------------------------------- /include/cg_snap.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_SNAP_H 2 | #define CG_SNAP_H 3 | 4 | void init_snap(void); 5 | 6 | void update_snap(void); 7 | 8 | void draw_snap(void); 9 | 10 | #endif // CG_SNAP_H 11 | -------------------------------------------------------------------------------- /include/cg_syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_SYSCALL_H 2 | #define CG_SYSCALL_H 3 | 4 | #include "ExportImport.h" 5 | 6 | #include 7 | 8 | EXPORTIMPORT void dllEntry(intptr_t(QDECL* syscallptr)(intptr_t arg, ...)); 9 | 10 | intptr_t QDECL CG_SysCalls(uint8_t* memoryBase, int32_t cmd, int32_t* args); 11 | 12 | #endif // CG_SYSCALL_H 13 | -------------------------------------------------------------------------------- /include/cg_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef CG_TIMER_H 2 | #define CG_TIMER_H 3 | 4 | void init_timer(void); 5 | 6 | void update_timer(void); 7 | 8 | void draw_timer(void); 9 | 10 | #endif // CG_TIMER_H 11 | -------------------------------------------------------------------------------- /include/cg_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #ifndef CG_UTILS_H 22 | #define CG_UTILS_H 23 | 24 | #include "cg_public.h" 25 | 26 | // TODO: remove this 27 | #define PSF_USERINPUT_NONE 0 28 | #define PSF_USERINPUT_FORWARD 1 29 | #define PSF_USERINPUT_BACKWARD 2 30 | #define PSF_USERINPUT_LEFT 8 31 | #define PSF_USERINPUT_RIGHT 16 32 | #define PSF_USERINPUT_JUMP 32 33 | #define PSF_USERINPUT_CROUCH 64 34 | #define PSF_USERINPUT_ATTACK 256 35 | #define PSF_USERINPUT_WALK 512 36 | 37 | snapshot_t const* getSnap(void); 38 | playerState_t const* getPs(void); 39 | 40 | #endif // CG_UTILS_H 41 | -------------------------------------------------------------------------------- /include/cg_vm.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | Note: mdd client proxymod contains code from Kevin Masterson a.k.a. CyberMind 21 | of the QMM - Q3 MultiMod 22 | */ 23 | #ifndef CG_VM_H 24 | #define CG_VM_H 25 | 26 | #include "q_shared.h" 27 | 28 | #define VM_MAGIC 0x12721444 29 | 30 | typedef enum 31 | { 32 | OP_UNDEF, 33 | OP_NOP, 34 | OP_BREAK, 35 | OP_ENTER, 36 | OP_LEAVE, 37 | OP_CALL, 38 | OP_PUSH, 39 | OP_POP, 40 | OP_CONST, 41 | OP_LOCAL, 42 | OP_JUMP, 43 | OP_EQ, 44 | OP_NE, 45 | OP_LTI, 46 | OP_LEI, 47 | OP_GTI, 48 | OP_GEI, 49 | OP_LTU, 50 | OP_LEU, 51 | OP_GTU, 52 | OP_GEU, 53 | OP_EQF, 54 | OP_NEF, 55 | OP_LTF, 56 | OP_LEF, 57 | OP_GTF, 58 | OP_GEF, 59 | OP_LOAD1, 60 | OP_LOAD2, 61 | OP_LOAD4, 62 | OP_STORE1, 63 | OP_STORE2, 64 | OP_STORE4, 65 | OP_ARG, 66 | OP_BLOCK_COPY, 67 | OP_SEX8, 68 | OP_SEX16, 69 | OP_NEGI, 70 | OP_ADD, 71 | OP_SUB, 72 | OP_DIVI, 73 | OP_DIVU, 74 | OP_MODI, 75 | OP_MODU, 76 | OP_MULI, 77 | OP_MULU, 78 | OP_BAND, 79 | OP_BOR, 80 | OP_BXOR, 81 | OP_BCOM, 82 | OP_LSH, 83 | OP_RSHI, 84 | OP_RSHU, 85 | OP_NEGF, 86 | OP_ADDF, 87 | OP_SUBF, 88 | OP_DIVF, 89 | OP_MULF, 90 | OP_CVIF, 91 | OP_CVFI 92 | } vmOps_t; 93 | 94 | typedef struct 95 | { 96 | int32_t vmMagic; 97 | 98 | int32_t instructionCount; 99 | 100 | int32_t codeOffset; 101 | int32_t codeLength; 102 | 103 | int32_t dataOffset; 104 | int32_t dataLength; 105 | int32_t litLength; // ( dataLength - litLength ) should be byteswapped on load 106 | int32_t bssLength; // zero filled memory appended to datalength 107 | } vmHeader_t; 108 | 109 | typedef struct vm_s 110 | { 111 | /* public interface */ 112 | char name[MAX_QPATH]; 113 | 114 | /* segments */ 115 | int32_t* codeSegment; /* code segment, each instruction is 2 ints */ 116 | byte* dataSegment; /* data segment, partially filled on load */ 117 | byte* stackSegment; /* stack segment */ 118 | 119 | /* status*/ 120 | int32_t codeSegmentLen; /* size of codeSegment */ 121 | int32_t dataSegmentLen; /* size of dataSegment */ 122 | int32_t dataSegmentMask; 123 | 124 | /* registers */ 125 | int32_t* opPointer; 126 | int32_t* opStack; 127 | int32_t opBase; 128 | 129 | /* memory */ 130 | int32_t memorySize; 131 | byte* memory; 132 | 133 | /* non-API function hooking */ 134 | int32_t hook_realfunc; /* address for a VM function to call after a hook completes (0 = don't call) */ 135 | } vm_t; 136 | 137 | extern vm_t g_VM; 138 | extern char vmpath[MAX_QPATH]; 139 | extern char vmbase[16]; 140 | extern int32_t vm_stacksize; 141 | 142 | intptr_t QDECL VM_Exec( 143 | vm_t* vm, 144 | int32_t command, 145 | int32_t arg0, 146 | int32_t arg1, 147 | int32_t arg2, 148 | int32_t arg3, 149 | int32_t arg4, 150 | int32_t arg5, 151 | int32_t arg6, 152 | int32_t arg7, 153 | int32_t arg8, 154 | int32_t arg9, 155 | int32_t arg10, 156 | int32_t arg11); 157 | qboolean VM_Create(vm_t* vm, char const* path, byte* oldmem); 158 | void VM_Destroy(vm_t* vm); 159 | qboolean VM_Restart(vm_t* vm, qboolean savemem); 160 | void* VM_ArgPtr(int32_t intValue); 161 | 162 | #endif // CG_VM_H 163 | -------------------------------------------------------------------------------- /include/g_local.h: -------------------------------------------------------------------------------- 1 | #ifndef G_LOCAL_H 2 | #define G_LOCAL_H 3 | 4 | #include "g_public.h" 5 | 6 | //============================================================================ 7 | 8 | typedef struct gentity_s gentity_t; 9 | 10 | struct gentity_s 11 | { 12 | // incomplete 13 | entityState_t s; // communicated by server to clients 14 | entityShared_t r; // shared by both the server system and game 15 | 16 | // DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER 17 | // EXPECTS THE FIELDS IN THAT ORDER! 18 | //================================ 19 | 20 | int32_t clipmask; // brushes with this content value will be collided against 21 | // when moving. items and corpses do not collide against 22 | // players, for instance 23 | 24 | // movers 25 | gentity_t const* parent; 26 | 27 | gentity_t* target_ent; 28 | 29 | int32_t nextthink; 30 | 31 | int32_t damage; 32 | int32_t splashDamage; // quad will increase this without increasing radius 33 | int32_t splashRadius; 34 | }; 35 | 36 | // 37 | // g_missile.c 38 | // 39 | void fire_grenade(gentity_t* bolt, gentity_t const* ent, vec3_t const start, vec3_t dir); 40 | void fire_rocket(gentity_t* bolt, gentity_t const* ent, vec3_t const start, vec3_t dir); 41 | 42 | // 43 | // g_weapon.c 44 | // 45 | void FireWeapon(playerState_t const* pm_ps, gentity_t* m, gentity_t const* ent); 46 | 47 | #endif // G_LOCAL_H 48 | -------------------------------------------------------------------------------- /include/g_public.h: -------------------------------------------------------------------------------- 1 | #ifndef G_PUBLIC_H 2 | #define G_PUBLIC_H 3 | 4 | #include "q_shared.h" 5 | 6 | // g_public.h -- game module information visible to server 7 | 8 | //=============================================================== 9 | 10 | typedef struct 11 | { 12 | // incomplete 13 | qboolean linked; // qfalse if not in any good cluster 14 | int32_t linkcount; 15 | 16 | int32_t svFlags; // SVF_NOCLIENT, SVF_BROADCAST, etc 17 | 18 | // only send to this client when SVF_SINGLECLIENT is set 19 | // if SVF_CLIENTMASK is set, use bitmask for clients to send to (maxclients must be <= 32, up to the mod to enforce 20 | // this) 21 | int32_t singleClient; 22 | 23 | qboolean bmodel; // if false, assume an explicit mins / maxs bounding box 24 | // only set by trap_SetBrushModel 25 | vec3_t mins, maxs; 26 | int32_t contents; // CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc 27 | // a non-solid entity should set to 0 28 | 29 | vec3_t absmin, absmax; // derived from mins/maxs and origin + rotation 30 | 31 | // currentOrigin will be used for all collision detection and world linking. 32 | // it will not necessarily be the same as the trajectory evaluation for the current 33 | // time, because each entity must be moved one at a time after time is advanced 34 | // to avoid simultaneous collision issues 35 | vec3_t currentOrigin; 36 | vec3_t currentAngles; 37 | 38 | // when a trace call is made and passEntityNum != ENTITYNUM_NONE, 39 | // an ent will be excluded from testing if: 40 | // ent->s.number == passEntityNum (don't interact with self) 41 | // ent->r.ownerNum == passEntityNum (don't interact with your own missiles) 42 | // entity[ent->r.ownerNum].r.ownerNum == passEntityNum (don't interact with other missiles from owner) 43 | int32_t ownerNum; 44 | } entityShared_t; 45 | 46 | #endif // G_PUBLIC_H 47 | -------------------------------------------------------------------------------- /include/nade_tracking.h: -------------------------------------------------------------------------------- 1 | #define MAX_NADES 10 2 | 3 | typedef struct 4 | { 5 | /** the entity number of the nade being tracked, -1 if not tracking */ 6 | int id; 7 | /** predicted time of when the nade will explode */ 8 | int explode_time; 9 | /** flag for whether this nade was seen in current snapshot */ 10 | int seen; 11 | } nade_info_t; 12 | 13 | extern nade_info_t nades[MAX_NADES]; 14 | -------------------------------------------------------------------------------- /include/q_math.h: -------------------------------------------------------------------------------- 1 | #ifndef Q_MATH_H 2 | #define Q_MATH_H 3 | 4 | #include "q_shared.h" 5 | 6 | #include 7 | 8 | /* This file was automatically generated. Do not edit! */ 9 | int Q_log2(int val); 10 | void Vector4Scale(vec4_t const in, vec_t scale, vec4_t out); 11 | void _VectorScale(vec3_t const in, vec_t scale, vec3_t out); 12 | void _VectorCopy(vec3_t const in, vec3_t out); 13 | void _VectorAdd(vec3_t const veca, vec3_t const vecb, vec3_t out); 14 | void _VectorSubtract(vec3_t const veca, vec3_t const vecb, vec3_t out); 15 | vec_t _DotProduct(vec3_t const v1, vec3_t const v2); 16 | void _VectorMA(vec3_t const veca, float scale, vec3_t const vecb, vec3_t vecc); 17 | void AddPointToBounds(vec3_t const v, vec3_t mins, vec3_t maxs); 18 | void ClearBounds(vec3_t mins, vec3_t maxs); 19 | float RadiusFromBounds(vec3_t const mins, vec3_t const maxs); 20 | #if defined __LCC__ || defined C_ONLY || !id386 || \ 21 | defined __VECTORC && !((defined __linux__ || __FreeBSD__) && (defined __i386__) && (!defined C_ONLY)) // rb010123 22 | int BoxOnPlaneSide(vec3_t const emins, vec3_t const emaxs, struct cplane_s* p); 23 | #endif 24 | #if !(defined __LCC__ || defined C_ONLY || !id386 || defined __VECTORC) && \ 25 | !((defined __linux__ || __FreeBSD__) && (defined __i386__) && (!defined C_ONLY)) // rb010123 26 | __declspec(naked) int BoxOnPlaneSide(vec3_t const emins, vec3_t const emaxs, struct cplane_s* p); 27 | #endif 28 | void SetPlaneSignbits(cplane_t* out); 29 | float AngleDelta(float angle1, float angle2); 30 | float AngleMod(float a); 31 | void AnglesSubtract(vec3_t const v1, vec3_t const v2, vec3_t v3); 32 | float AngleSubtract(float a1, float a2); 33 | float LerpAngle(float from, float to, float frac); 34 | void VectorRotate(vec3_t const in, vec3_t matrix[3], vec3_t out); 35 | void MakeNormalVectors(vec3_t const forward, vec3_t right, vec3_t up); 36 | #if !idppc 37 | float Q_fabs(float f); 38 | #endif 39 | void ProjectPointOnPlane(vec3_t dst, vec3_t const p, vec3_t const normal); 40 | void RotateAroundDirection(vec3_t axis[3], float yaw); 41 | void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]); 42 | qboolean PlaneFromPoints(vec4_t plane, vec3_t const a, vec3_t const b, vec3_t const c); 43 | float NormalizeColor(vec3_t const in, vec3_t out); 44 | void ByteToDir(int b, vec3_t dir); 45 | int DirToByte(vec3_t const dir); 46 | signed short ClampShort(int i); 47 | signed char ClampChar(int i); 48 | #if !idppc 49 | float Q_rsqrt(float number); 50 | #endif 51 | 52 | #endif // Q_MATH_H 53 | -------------------------------------------------------------------------------- /include/surfaceflags.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 1999-2000 Id Software, Inc. 2 | // 3 | // This file must be identical in the quake and utils directories 4 | #ifndef SURFACEFLAGS_H 5 | #define SURFACEFLAGS_H 6 | 7 | // contents flags are separate bits 8 | // a given brush can contribute multiple content bits 9 | 10 | // these definitions also need to be in q_shared.h! 11 | 12 | #define CONTENTS_SOLID 1 // an eye is never valid in a solid 13 | #define CONTENTS_LAVA 8 14 | #define CONTENTS_SLIME 16 15 | #define CONTENTS_WATER 32 16 | #define CONTENTS_FOG 64 17 | 18 | #define CONTENTS_NOTTEAM1 0x0080 19 | #define CONTENTS_NOTTEAM2 0x0100 20 | #define CONTENTS_NOBOTCLIP 0x0200 21 | 22 | #define CONTENTS_AREAPORTAL 0x8000 23 | 24 | #define CONTENTS_PLAYERCLIP 0x10000 25 | #define CONTENTS_MONSTERCLIP 0x20000 26 | // bot specific contents types 27 | #define CONTENTS_TELEPORTER 0x40000 28 | #define CONTENTS_JUMPPAD 0x80000 29 | #define CONTENTS_CLUSTERPORTAL 0x100000 30 | #define CONTENTS_DONOTENTER 0x200000 31 | #define CONTENTS_BOTCLIP 0x400000 32 | #define CONTENTS_MOVER 0x800000 33 | 34 | #define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity 35 | 36 | #define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game 37 | #define CONTENTS_CORPSE 0x4000000 38 | #define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp 39 | #define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp 40 | #define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside 41 | #define CONTENTS_TRIGGER 0x40000000 42 | #define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava) 43 | 44 | #define SURF_NODAMAGE 0x1 // never give falling damage 45 | #define SURF_SLICK 0x2 // effects game physics 46 | #define SURF_SKY 0x4 // lighting from environment map 47 | #define SURF_LADDER 0x8 48 | #define SURF_NOIMPACT 0x10 // don't make missile explosions 49 | #define SURF_NOMARKS 0x20 // don't leave missile marks 50 | #define SURF_FLESH 0x40 // make flesh sounds and effects 51 | #define SURF_NODRAW 0x80 // don't generate a drawsurface at all 52 | #define SURF_HINT 0x100 // make a primary bsp splitter 53 | #define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes 54 | #define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap 55 | #define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes 56 | #define SURF_METALSTEPS 0x1000 // clanking footsteps 57 | #define SURF_NOSTEPS 0x2000 // no footstep sounds 58 | #define SURF_NONSOLID 0x4000 // don't collide against curves with this set 59 | #define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light 60 | #define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map 61 | #define SURF_NODLIGHT 0x20000 // don't dlight even if solid (solid lava, skies) 62 | #define SURF_DUST 0x40000 // leave a dust trail when walking on this surface 63 | 64 | #endif // SURFACEFLAGS_H 65 | -------------------------------------------------------------------------------- /include/tr_types.h: -------------------------------------------------------------------------------- 1 | #ifndef TR_TYPES_H 2 | #define TR_TYPES_H 3 | 4 | #include "q_shared.h" 5 | 6 | #define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces 7 | 8 | #define REFENTITYNUM_BITS 10 // can't be increased without changing drawsurf bit packing 9 | #define REFENTITYNUM_MASK ((1 << REFENTITYNUM_BITS) - 1) 10 | // the last N-bit number (2^REFENTITYNUM_BITS - 1) is reserved for the special world refentity, 11 | // and this is reflected by the value of MAX_REFENTITIES (which therefore is not a power-of-2) 12 | #define MAX_REFENTITIES ((1 << REFENTITYNUM_BITS) - 1) 13 | #define REFENTITYNUM_WORLD ((1 << REFENTITYNUM_BITS) - 1) 14 | 15 | // renderfx flags 16 | #define RF_MINLIGHT 0x0001 // allways have some light (viewmodel, some items) 17 | #define RF_THIRD_PERSON 0x0002 // don't draw through eyes, only mirrors (player bodies, chat sprites) 18 | #define RF_FIRST_PERSON 0x0004 // only draw through eyes (view weapon, damage blood blob) 19 | #define RF_DEPTHHACK 0x0008 // for view weapon Z crunching 20 | 21 | #define RF_CROSSHAIR \ 22 | 0x0010 // This item is a cross hair and will draw over everything similar to 23 | // DEPTHHACK in stereo rendering mode, with the difference that the 24 | // projection matrix won't be hacked to reduce the stereo separation as 25 | // is done for the gun. 26 | 27 | #define RF_NOSHADOW 0x0040 // don't add stencil shadows 28 | 29 | #define RF_LIGHTING_ORIGIN \ 30 | 0x0080 // use refEntity->lightingOrigin instead of refEntity->origin 31 | // for lighting. This allows entities to sink into the floor 32 | // with their origin going solid, and allows all parts of a 33 | // player to get the same lighting 34 | 35 | #define RF_SHADOW_PLANE 0x0100 // use refEntity->shadowPlane 36 | #define RF_WRAP_FRAMES \ 37 | 0x0200 // mod the model frames by the maxframes to allow continuous 38 | // animation without needing to know the frame count 39 | 40 | // refdef flags 41 | #define RDF_NOWORLDMODEL 0x0001 // used for player configuration screen 42 | #define RDF_HYPERSPACE 0x0004 // teleportation effect 43 | 44 | typedef struct 45 | { 46 | vec3_t xyz; 47 | float st[2]; 48 | byte modulate[4]; 49 | } polyVert_t; 50 | 51 | typedef struct poly_s 52 | { 53 | qhandle_t hShader; 54 | int32_t numVerts; 55 | polyVert_t* verts; 56 | } poly_t; 57 | 58 | typedef enum 59 | { 60 | RT_MODEL, 61 | RT_POLY, 62 | RT_SPRITE, 63 | RT_BEAM, 64 | RT_RAIL_CORE, 65 | RT_RAIL_RINGS, 66 | RT_LIGHTNING, 67 | RT_PORTALSURFACE, // doesn't draw anything, just info for portals 68 | 69 | RT_MAX_REF_ENTITY_TYPE 70 | } refEntityType_t; 71 | 72 | typedef struct 73 | { 74 | refEntityType_t reType; 75 | int32_t renderfx; 76 | 77 | qhandle_t hModel; // opaque type outside refresh 78 | 79 | // most recent data 80 | vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) 81 | float shadowPlane; // projection shadows go here, stencils go slightly lower 82 | 83 | vec3_t axis[3]; // rotation vectors 84 | qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale 85 | float origin[3]; // also used as MODEL_BEAM's "from" 86 | int32_t frame; // also used as MODEL_BEAM's diameter 87 | 88 | // previous data for frame interpolation 89 | float oldorigin[3]; // also used as MODEL_BEAM's "to" 90 | int32_t oldframe; 91 | float backlerp; // 0.0 = current, 1.0 = old 92 | 93 | // texturing 94 | int32_t skinNum; // inline skin index 95 | qhandle_t customSkin; // NULL for default skin 96 | qhandle_t customShader; // use one image for the entire thing 97 | 98 | // misc 99 | byte shaderRGBA[4]; // colors used by rgbgen entity shaders 100 | float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers 101 | float shaderTime; // subtracted from refdef time to control effect start times 102 | 103 | // extra sprite information 104 | float radius; 105 | float rotation; 106 | } refEntity_t; 107 | 108 | #define MAX_RENDER_STRINGS 8 109 | #define MAX_RENDER_STRING_LENGTH 32 110 | 111 | typedef struct 112 | { 113 | int32_t x, y, width, height; 114 | float fov_x, fov_y; 115 | vec3_t vieworg; 116 | vec3_t viewaxis[3]; // transformation matrix 117 | 118 | // time in milliseconds for shader effects and other time dependent rendering issues 119 | int32_t time; 120 | 121 | int32_t rdflags; // RDF_NOWORLDMODEL, etc 122 | 123 | // 1 bits will prevent the associated area from rendering at all 124 | byte areamask[MAX_MAP_AREA_BYTES]; 125 | 126 | // text messages for deform text shaders 127 | char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; 128 | } refdef_t; 129 | 130 | typedef enum 131 | { 132 | STEREO_CENTER, 133 | STEREO_LEFT, 134 | STEREO_RIGHT 135 | } stereoFrame_t; 136 | 137 | /* 138 | ** glconfig_t 139 | ** 140 | ** Contains variables specific to the OpenGL configuration 141 | ** being run right now. These are constant once the OpenGL 142 | ** subsystem is initialized. 143 | */ 144 | typedef enum 145 | { 146 | TC_NONE, 147 | TC_S3TC 148 | } textureCompression_t; 149 | 150 | typedef enum 151 | { 152 | GLDRV_ICD, // driver is integrated with window system 153 | // WARNING: there are tests that check for 154 | // > GLDRV_ICD for minidriverness, so this 155 | // should always be the lowest value in this 156 | // enum set 157 | GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver 158 | GLDRV_VOODOO // driver is a 3Dfx standalone driver 159 | } glDriverType_t; 160 | 161 | typedef enum 162 | { 163 | GLHW_GENERIC, // where everything works the way it should 164 | GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is 165 | // the hardware type then there can NOT exist a secondary 166 | // display adapter 167 | GLHW_RIVA128, // where you can't interpolate alpha 168 | GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures 169 | GLHW_PERMEDIA2 // where you don't have src*dst 170 | } glHardwareType_t; 171 | 172 | typedef struct 173 | { 174 | char renderer_string[MAX_STRING_CHARS]; 175 | char vendor_string[MAX_STRING_CHARS]; 176 | char version_string[MAX_STRING_CHARS]; 177 | char extensions_string[BIG_INFO_STRING]; 178 | 179 | int32_t maxTextureSize; // queried from GL 180 | int32_t maxActiveTextures; // multitexture ability 181 | 182 | int32_t colorBits, depthBits, stencilBits; 183 | 184 | glDriverType_t driverType; 185 | glHardwareType_t hardwareType; 186 | 187 | qboolean deviceSupportsGamma; 188 | textureCompression_t textureCompression; 189 | qboolean textureEnvAddAvailable; 190 | 191 | int32_t vidWidth, vidHeight; 192 | // aspect is the screen's physical width / height, which may be different 193 | // than scrWidth / scrHeight if the pixels are non-square 194 | // normal screens should be 4/3, but wide aspect monitors may be 16/9 195 | float windowAspect; 196 | 197 | int32_t displayFrequency; 198 | 199 | // synonymous with "does rendering consume the entire screen?", therefore 200 | // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that 201 | // used CDS. 202 | qboolean isFullscreen; 203 | qboolean stereoEnabled; 204 | qboolean smpActive; // dual processor 205 | } glconfig_t; 206 | 207 | #endif // TR_TYPES_H 208 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | add_library(cgame_obj OBJECT 4 | bbox.c 5 | bg_misc.c 6 | bg_pmove.c 7 | cg_ammo.c 8 | cg_cgaz.c 9 | cg_consolecmds.c 10 | cg_cvar.c 11 | cg_draw.c 12 | cg_entity.c 13 | cg_gl.c 14 | cg_hud.c 15 | cg_jump.c 16 | cg_main.c 17 | cg_marks.c 18 | cg_rl.c 19 | cg_snap.c 20 | cg_syscall.c 21 | cg_timer.c 22 | cg_utils.c 23 | cg_view.c 24 | cg_vm.c 25 | compass.c 26 | defrag.c 27 | g_missile.c 28 | g_weapon.c 29 | help.c 30 | pitch.c 31 | q_math.c 32 | q_shared.c 33 | ) 34 | 35 | target_include_directories(cgame_obj 36 | PUBLIC ../include 37 | ) 38 | 39 | target_compile_definitions(cgame_obj PRIVATE BUILDING_SHARED) 40 | 41 | target_compile_features(cgame_obj PUBLIC c_std_11) 42 | 43 | if(MSVC) 44 | target_compile_options(cgame_obj PRIVATE 45 | /WX 46 | /W4 47 | /wd4996 48 | /wd5105 # disable Windows SDK 10.0.19041.0 warnings with C11 49 | ) 50 | target_link_options(cgame_obj PRIVATE /WX) 51 | else() 52 | target_compile_options(cgame_obj PRIVATE 53 | -Werror 54 | -Wall 55 | -Wextra 56 | -pedantic-errors 57 | -Wmissing-prototypes 58 | -Wshadow 59 | -Wstrict-prototypes 60 | 61 | $<$:-Wunreachable-code-return> 62 | $<$:-Wunreachable-code> 63 | ) 64 | endif() 65 | 66 | set_target_properties(cgame_obj PROPERTIES 67 | C_VISIBILITY_PRESET hidden 68 | POSITION_INDEPENDENT_CODE ON 69 | ) 70 | 71 | add_library(${BINARY_NAME} SHARED $) 72 | set_target_properties(${BINARY_NAME} PROPERTIES 73 | PREFIX "" IMPORT_PREFIX "" # remove "lib" prefix 74 | ) 75 | install(TARGETS ${BINARY_NAME} DESTINATION .) 76 | -------------------------------------------------------------------------------- /src/bbox.c: -------------------------------------------------------------------------------- 1 | #include "bbox.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_local.h" 5 | #include "cg_utils.h" 6 | #include "help.h" 7 | 8 | static vmCvar_t bbox; 9 | static vmCvar_t bbox_rgba; 10 | 11 | static cvarTable_t bbox_cvars[] = { 12 | { &bbox, "mdd_bbox", "0", CVAR_ARCHIVE_ND }, 13 | { &bbox_rgba, "mdd_bbox_rgba", ".9 .5 .7 .7", CVAR_ARCHIVE_ND }, 14 | }; 15 | 16 | static help_t bbox_help[] = { 17 | { 18 | bbox_cvars + 1, 19 | RGBA, 20 | { 21 | "mdd_bbox_rgba X X X X", 22 | }, 23 | }, 24 | }; 25 | 26 | typedef struct 27 | { 28 | vec4_t bbox_rgba; 29 | playerState_t const* pm_ps; 30 | 31 | // qhandle_t bboxShader; 32 | qhandle_t bboxShader_nocull; 33 | 34 | vec3_t mins; 35 | vec3_t maxs; 36 | 37 | float extx; 38 | float exty; 39 | float extz; 40 | 41 | polyVert_t verts[4]; 42 | } bbox_t; 43 | 44 | static bbox_t s; 45 | 46 | void init_bbox(void) 47 | { 48 | init_cvars(bbox_cvars, ARRAY_LEN(bbox_cvars)); 49 | init_help(bbox_help, ARRAY_LEN(bbox_help)); 50 | 51 | // get the shader handles 52 | // s.bboxShader = trap_R_RegisterShader("bbox"); 53 | s.bboxShader_nocull = trap_R_RegisterShader("bbox_nocull"); 54 | 55 | s.mins[0] = s.mins[1] = -15; 56 | s.mins[2] = -24; 57 | s.maxs[0] = s.maxs[1] = 15; 58 | 59 | // get the extents (size) 60 | s.extx = s.maxs[0] - s.mins[0]; 61 | s.exty = s.maxs[1] - s.mins[1]; 62 | 63 | // set the polygon's texture coordinates 64 | s.verts[0].st[0] = 0; 65 | s.verts[0].st[1] = 0; 66 | s.verts[1].st[0] = 0; 67 | s.verts[1].st[1] = 1; 68 | s.verts[2].st[0] = 1; 69 | s.verts[2].st[1] = 1; 70 | s.verts[3].st[0] = 1; 71 | s.verts[3].st[1] = 0; 72 | } 73 | 74 | void update_bbox(void) 75 | { 76 | update_cvars(bbox_cvars, ARRAY_LEN(bbox_cvars)); 77 | } 78 | 79 | static void CG_AddBoundingBox(void); 80 | 81 | void draw_bbox(void) 82 | { 83 | if (!bbox.integer) return; 84 | 85 | ParseVec(bbox_rgba.string, s.bbox_rgba, 4); 86 | 87 | s.pm_ps = getPs(); 88 | 89 | CG_AddBoundingBox(); 90 | } 91 | 92 | static void CG_AddBoundingBox(void) 93 | { 94 | int i; 95 | vec3_t corners[8]; 96 | // 4 ----- 7 97 | // z /| /| 98 | // | _ y 5 ----- 6 | 99 | // / | | | | 100 | // x | 0 ----| 3 101 | // |/ |/ 102 | // 1 ----- 2 103 | 104 | // TODO 105 | // don't draw it in third-person 106 | // if (!cg_thirdPerson.integer) 107 | // return; 108 | 109 | // don't draw it for dead players 110 | if (s.pm_ps->pm_type >= PM_SPECTATOR) return; 111 | 112 | // if they don't exist, forget it 113 | if (/*!s.bboxShader || */ !s.bboxShader_nocull) return; 114 | 115 | if (s.pm_ps->pm_flags & PMF_DUCKED) 116 | { 117 | s.maxs[2] = 16; 118 | } 119 | else 120 | { 121 | s.maxs[2] = 32; 122 | } 123 | 124 | // get the extents (size) 125 | s.extz = s.maxs[2] - s.mins[2]; 126 | 127 | // set the polygon's vertex colors 128 | for (i = 0; i < 4; ++i) 129 | { 130 | s.verts[i].modulate[0] = (byte)(s.bbox_rgba[0] * 255); 131 | s.verts[i].modulate[1] = (byte)(s.bbox_rgba[1] * 255); 132 | s.verts[i].modulate[2] = (byte)(s.bbox_rgba[2] * 255); 133 | s.verts[i].modulate[3] = (byte)(s.bbox_rgba[3] * 255); 134 | } 135 | 136 | VectorAdd(s.pm_ps->origin, s.mins, corners[0]); 137 | 138 | VectorCopy(corners[0], corners[1]); 139 | corners[1][0] += s.extx; 140 | 141 | VectorCopy(corners[1], corners[2]); 142 | corners[2][1] += s.exty; 143 | 144 | VectorCopy(corners[2], corners[3]); 145 | corners[3][0] -= s.extx; 146 | 147 | // bottom 148 | VectorCopy(corners[0], s.verts[0].xyz); 149 | VectorCopy(corners[1], s.verts[1].xyz); 150 | VectorCopy(corners[2], s.verts[2].xyz); 151 | VectorCopy(corners[3], s.verts[3].xyz); 152 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 153 | 154 | if (bbox.integer == 1) 155 | { 156 | for (i = 0; i < 4; ++i) 157 | { 158 | VectorCopy(corners[i], corners[i + 4]); 159 | corners[i + 4][2] += s.extz; 160 | } 161 | 162 | // top 163 | VectorCopy(corners[7], s.verts[0].xyz); 164 | VectorCopy(corners[6], s.verts[1].xyz); 165 | VectorCopy(corners[5], s.verts[2].xyz); 166 | VectorCopy(corners[4], s.verts[3].xyz); 167 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 168 | 169 | // top side 170 | VectorCopy(corners[3], s.verts[0].xyz); 171 | VectorCopy(corners[2], s.verts[1].xyz); 172 | VectorCopy(corners[6], s.verts[2].xyz); 173 | VectorCopy(corners[7], s.verts[3].xyz); 174 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 175 | 176 | // left side 177 | VectorCopy(corners[2], s.verts[0].xyz); 178 | VectorCopy(corners[1], s.verts[1].xyz); 179 | VectorCopy(corners[5], s.verts[2].xyz); 180 | VectorCopy(corners[6], s.verts[3].xyz); 181 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 182 | 183 | // right side 184 | VectorCopy(corners[0], s.verts[0].xyz); 185 | VectorCopy(corners[3], s.verts[1].xyz); 186 | VectorCopy(corners[7], s.verts[2].xyz); 187 | VectorCopy(corners[4], s.verts[3].xyz); 188 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 189 | 190 | // bottom side 191 | VectorCopy(corners[1], s.verts[0].xyz); 192 | VectorCopy(corners[0], s.verts[1].xyz); 193 | VectorCopy(corners[4], s.verts[2].xyz); 194 | VectorCopy(corners[5], s.verts[3].xyz); 195 | trap_R_AddPolyToScene(s.bboxShader_nocull, 4, s.verts); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/bbox.h: -------------------------------------------------------------------------------- 1 | #ifndef BBOX_H 2 | #define BBOX_H 3 | 4 | void init_bbox(void); 5 | 6 | void update_bbox(void); 7 | 8 | void draw_bbox(void); 9 | 10 | #endif // BBOX_H 11 | -------------------------------------------------------------------------------- /src/bg_misc.c: -------------------------------------------------------------------------------- 1 | #include "bg_public.h" 2 | #include "q_math.h" 3 | 4 | /* 5 | ================ 6 | BG_EvaluateTrajectory 7 | 8 | ================ 9 | */ 10 | void BG_EvaluateTrajectory(trajectory_t const* tr, int32_t atTime, vec3_t result) 11 | { 12 | float deltaTime; 13 | float phase; 14 | 15 | switch (tr->trType) 16 | { 17 | case TR_STATIONARY: 18 | case TR_INTERPOLATE: 19 | VectorCopy(tr->trBase, result); 20 | break; 21 | case TR_LINEAR: 22 | deltaTime = (atTime - tr->trTime) * .001f; // milliseconds to seconds 23 | VectorMA(tr->trBase, deltaTime, tr->trDelta, result); 24 | break; 25 | case TR_SINE: 26 | deltaTime = (atTime - tr->trTime) / (float)tr->trDuration; 27 | phase = sinf(deltaTime * (float)M_PI * 2); 28 | VectorMA(tr->trBase, phase, tr->trDelta, result); 29 | break; 30 | case TR_LINEAR_STOP: 31 | // milliseconds to seconds 32 | deltaTime = ((atTime > tr->trTime + tr->trDuration ? tr->trTime + tr->trDuration : atTime) - tr->trTime) * .001f; 33 | if (deltaTime < 0) 34 | { 35 | deltaTime = 0; 36 | } 37 | VectorMA(tr->trBase, deltaTime, tr->trDelta, result); 38 | break; 39 | case TR_GRAVITY: 40 | deltaTime = (atTime - tr->trTime) * .001f; // milliseconds to seconds 41 | VectorMA(tr->trBase, deltaTime, tr->trDelta, result); 42 | result[2] -= .5f * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity... 43 | break; 44 | default: 45 | // Com_Error(ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trType); 46 | break; 47 | } 48 | } 49 | 50 | /* 51 | ================ 52 | BG_EvaluateTrajectoryDelta 53 | 54 | For determining velocity at a given time 55 | ================ 56 | */ 57 | void BG_EvaluateTrajectoryDelta(trajectory_t const* tr, int32_t atTime, vec3_t result) 58 | { 59 | float deltaTime; 60 | float phase; 61 | 62 | switch (tr->trType) 63 | { 64 | case TR_STATIONARY: 65 | case TR_INTERPOLATE: 66 | VectorClear(result); 67 | break; 68 | case TR_LINEAR: 69 | VectorCopy(tr->trDelta, result); 70 | break; 71 | case TR_SINE: 72 | deltaTime = (atTime - tr->trTime) / (float)tr->trDuration; 73 | phase = cosf(deltaTime * (float)M_PI * 2); // derivative of sin = cos 74 | phase *= .5f; 75 | VectorScale(tr->trDelta, phase, result); 76 | break; 77 | case TR_LINEAR_STOP: 78 | if (atTime > tr->trTime + tr->trDuration) 79 | { 80 | VectorClear(result); 81 | return; 82 | } 83 | VectorCopy(tr->trDelta, result); 84 | break; 85 | case TR_GRAVITY: 86 | deltaTime = (atTime - tr->trTime) * .001f; // milliseconds to seconds 87 | VectorCopy(tr->trDelta, result); 88 | result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity... 89 | break; 90 | default: 91 | // Com_Error(ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trType); 92 | break; 93 | } 94 | } 95 | 96 | /* 97 | ======================== 98 | BG_PlayerStateToEntityState 99 | 100 | This is done after each set of usercmd_t on the server, 101 | and after local prediction on the client 102 | ======================== 103 | */ 104 | void BG_PlayerStateToEntityState(playerState_t const* pm_ps, entityState_t* s, qboolean snap) 105 | { 106 | s->number = pm_ps->clientNum; 107 | 108 | s->pos.trType = TR_INTERPOLATE; 109 | VectorCopy(pm_ps->origin, s->pos.trBase); 110 | if (snap) 111 | { 112 | SnapVector(s->pos.trBase); 113 | } 114 | // set the trDelta for flag direction 115 | VectorCopy(pm_ps->velocity, s->pos.trDelta); 116 | 117 | s->apos.trType = TR_INTERPOLATE; 118 | VectorCopy(pm_ps->viewangles, s->apos.trBase); 119 | if (snap) 120 | { 121 | SnapVector(s->apos.trBase); 122 | } 123 | 124 | s->eFlags = pm_ps->eFlags; 125 | 126 | s->weapon = pm_ps->weapon; 127 | s->groundEntityNum = pm_ps->groundEntityNum; 128 | } 129 | -------------------------------------------------------------------------------- /src/bg_pmove.c: -------------------------------------------------------------------------------- 1 | #include "bg_pmove.h" 2 | 3 | #include "cg_local.h" 4 | 5 | #include 6 | 7 | /* 8 | ============ 9 | PM_CmdScale 10 | 11 | Returns the scale factor to apply to cmd movements 12 | This allows the clients to use axial -127 to 127 values for all directions 13 | without getting a sqrt(2) distortion in speed. 14 | ============ 15 | */ 16 | float PM_CmdScale(playerState_t const* pm_ps, usercmd_t const* cmd) 17 | { 18 | int32_t max = abs(cmd->forwardmove); 19 | if (abs(cmd->rightmove) > max) 20 | { 21 | max = abs(cmd->rightmove); 22 | } 23 | if (abs(cmd->upmove) > max) 24 | { 25 | max = abs(cmd->upmove); 26 | } 27 | if (!max) 28 | { 29 | return 0; 30 | } 31 | 32 | float const total = 33 | sqrtf((float)(cmd->forwardmove * cmd->forwardmove + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove)); 34 | return (float)pm_ps->speed * max / (127.f * total); 35 | } 36 | 37 | /* PM_CmdScale without upmove */ 38 | float PM_AltCmdScale(playerState_t const* pm_ps, usercmd_t const* cmd) 39 | { 40 | int32_t max = abs(cmd->forwardmove); 41 | if (abs(cmd->rightmove) > max) 42 | { 43 | max = abs(cmd->rightmove); 44 | } 45 | if (!max) 46 | { 47 | return 0; 48 | } 49 | 50 | float const total = sqrtf((float)(cmd->forwardmove * cmd->forwardmove + cmd->rightmove * cmd->rightmove)); 51 | return (float)pm_ps->speed * max / (127.f * total); 52 | } 53 | 54 | /* 55 | ============= 56 | PM_CheckJump 57 | ============= 58 | */ 59 | qboolean PM_CheckJump(pmove_t* pm, playerState_t* pm_ps, pml_t* pml) 60 | { 61 | if (pm_ps->pm_flags & PMF_RESPAWNED) 62 | { 63 | return qfalse; // don't allow jump until all buttons are up 64 | } 65 | 66 | if (pm->cmd.upmove < 10) 67 | { 68 | // not holding jump 69 | return qfalse; 70 | } 71 | 72 | // must wait for jump to be released 73 | if (pm_ps->pm_flags & PMF_JUMP_HELD) 74 | { 75 | // clear upmove so cmdscale doesn't lower running speed 76 | pm->cmd.upmove = 0; 77 | return qfalse; 78 | } 79 | 80 | pml->groundPlane = qfalse; // jumping away 81 | pml->walking = qfalse; 82 | pm_ps->pm_flags |= PMF_JUMP_HELD; 83 | 84 | pm_ps->groundEntityNum = ENTITYNUM_NONE; 85 | pm_ps->velocity[2] = JUMP_VELOCITY; 86 | 87 | return qtrue; 88 | } 89 | 90 | /* 91 | ============= 92 | PM_CorrectAllSolid 93 | ============= 94 | */ 95 | static qboolean PM_CorrectAllSolid(pmove_t* pm, playerState_t* pm_ps, pml_t* pml, trace_t* trace) 96 | { 97 | vec3_t point; 98 | 99 | // if (pm->debugLevel) 100 | // { 101 | // Com_Printf("%i:allsolid\n", c_pmove); 102 | // } 103 | 104 | // jitter around 105 | for (int8_t i = -1; i <= 1; ++i) 106 | { 107 | for (int8_t j = -1; j <= 1; ++j) 108 | { 109 | for (int8_t k = -1; k <= 1; ++k) 110 | { 111 | VectorCopy(pm_ps->origin, point); 112 | point[0] += (float)i; 113 | point[1] += (float)j; 114 | point[2] += (float)k; 115 | trap_CM_BoxTrace(trace, point, point, pm->mins, pm->maxs, 0, pm->tracemask); 116 | if (!trace->allsolid) 117 | { 118 | point[0] = pm_ps->origin[0]; 119 | point[1] = pm_ps->origin[1]; 120 | point[2] = pm_ps->origin[2] - .25f; 121 | 122 | trap_CM_BoxTrace(trace, pm_ps->origin, point, pm->mins, pm->maxs, 0, pm->tracemask); 123 | pml->groundTrace = *trace; 124 | return qtrue; 125 | } 126 | } 127 | } 128 | } 129 | 130 | pm_ps->groundEntityNum = ENTITYNUM_NONE; 131 | pml->groundPlane = qfalse; 132 | pml->walking = qfalse; 133 | 134 | return qfalse; 135 | } 136 | 137 | /* 138 | ============= 139 | PM_GroundTraceMissed 140 | 141 | The ground trace didn't hit a surface, so we are in freefall 142 | ============= 143 | */ 144 | static void PM_GroundTraceMissed(playerState_t* pm_ps, pml_t* pml) 145 | { 146 | pm_ps->groundEntityNum = ENTITYNUM_NONE; 147 | pml->groundPlane = qfalse; 148 | pml->walking = qfalse; 149 | } 150 | 151 | /* 152 | ============= 153 | PM_GroundTrace 154 | ============= 155 | */ 156 | void PM_GroundTrace(pmove_t* pm, playerState_t* pm_ps, pml_t* pml) 157 | { 158 | vec3_t point; 159 | trace_t trace; 160 | 161 | point[0] = pm_ps->origin[0]; 162 | point[1] = pm_ps->origin[1]; 163 | point[2] = pm_ps->origin[2] - .25f; 164 | 165 | trap_CM_BoxTrace(&trace, pm_ps->origin, point, pm->mins, pm->maxs, 0, pm->tracemask); 166 | pml->groundTrace = trace; 167 | 168 | // do something corrective if the trace starts in a solid... 169 | if (trace.allsolid) 170 | { 171 | if (!PM_CorrectAllSolid(pm, pm_ps, pml, &trace)) return; 172 | } 173 | 174 | // if the trace didn't hit anything, we are in free fall 175 | if (trace.fraction == 1.f) 176 | { 177 | PM_GroundTraceMissed(pm_ps, pml); 178 | return; 179 | } 180 | 181 | // check if getting thrown off the ground 182 | if (pm_ps->velocity[2] > 0 && DotProduct(pm_ps->velocity, trace.plane.normal) > 10) 183 | { 184 | // if ( pm->debugLevel ) { 185 | // Com_Printf("%i:kickoff\n", c_pmove); 186 | // } 187 | pm_ps->groundEntityNum = ENTITYNUM_NONE; 188 | pml->groundPlane = qfalse; 189 | pml->walking = qfalse; 190 | return; 191 | } 192 | 193 | // slopes that are too steep will not be considered onground 194 | if (trace.plane.normal[2] < MIN_WALK_NORMAL) 195 | { 196 | // if ( pm->debugLevel ) { 197 | // Com_Printf("%i:steep\n", c_pmove); 198 | // } 199 | // FIXME: if they can't slide down the slope, let them 200 | // walk (sharp crevices) 201 | pm_ps->groundEntityNum = ENTITYNUM_NONE; 202 | pml->groundPlane = qtrue; 203 | pml->walking = qfalse; 204 | return; 205 | } 206 | 207 | pml->groundPlane = qtrue; 208 | pml->walking = qtrue; 209 | 210 | // hitting solid ground will end a waterjump 211 | if (pm_ps->pm_flags & PMF_TIME_WATERJUMP) 212 | { 213 | pm_ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); 214 | pm_ps->pm_time = 0; 215 | } 216 | 217 | if (pm_ps->groundEntityNum == ENTITYNUM_NONE) 218 | { 219 | // just hit the ground 220 | // if ( pm->debugLevel ) { 221 | // Com_Printf("%i:Land\n", c_pmove); 222 | // } 223 | 224 | // PM_CrashLand(); 225 | 226 | // don't do landing time if we were just going down a slope 227 | if (pml->previous_velocity[2] < -200) 228 | { 229 | // don't allow another jump for a little while 230 | pm_ps->pm_flags |= PMF_TIME_LAND; 231 | pm_ps->pm_time = 250; 232 | } 233 | } 234 | 235 | pm_ps->groundEntityNum = trace.entityNum; 236 | 237 | // don't reset the z velocity for slopes 238 | // pm.ps->velocity[2] = 0; 239 | 240 | // PM_AddTouchEnt(trace.entityNum); 241 | } 242 | 243 | /* 244 | ============= 245 | PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving 246 | ============= 247 | */ 248 | void PM_SetWaterLevel(pmove_t* pm, playerState_t* pm_ps) 249 | { 250 | vec3_t point; 251 | int cont; 252 | int sample1; 253 | int sample2; 254 | 255 | // get waterlevel, accounting for ducking 256 | pm->waterlevel = 0; 257 | pm->watertype = 0; 258 | 259 | point[0] = pm_ps->origin[0]; 260 | point[1] = pm_ps->origin[1]; 261 | point[2] = pm_ps->origin[2] + MINS_Z + 1; 262 | 263 | cont = trap_CM_PointContents(point, 0); 264 | 265 | if (cont & MASK_WATER) 266 | { 267 | sample2 = pm_ps->viewheight - MINS_Z; 268 | sample1 = sample2 / 2; 269 | 270 | pm->watertype = cont; 271 | pm->waterlevel = 1; 272 | point[2] = pm_ps->origin[2] + MINS_Z + sample1; 273 | cont = trap_CM_PointContents(point, 0); 274 | if (cont & MASK_WATER) 275 | { 276 | pm->waterlevel = 2; 277 | point[2] = pm_ps->origin[2] + MINS_Z + sample2; 278 | cont = trap_CM_PointContents(point, 0); 279 | if (cont & MASK_WATER) 280 | { 281 | pm->waterlevel = 3; 282 | } 283 | } 284 | } 285 | } 286 | 287 | /* 288 | ============== 289 | PM_CheckDuck 290 | 291 | Sets mins, maxs, and pm_ps.viewheight 292 | ============== 293 | */ 294 | void PM_CheckDuck(pmove_t* pm, playerState_t* pm_ps) 295 | { 296 | trace_t trace; 297 | 298 | if (pm_ps->powerups[PW_INVULNERABILITY]) 299 | { 300 | if (pm_ps->pm_flags & PMF_INVULEXPAND) 301 | { 302 | // invulnerability sphere has a 42 units radius 303 | VectorSet(pm->mins, -42, -42, -42); 304 | VectorSet(pm->maxs, 42, 42, 42); 305 | } 306 | else 307 | { 308 | VectorSet(pm->mins, -15, -15, MINS_Z); 309 | VectorSet(pm->maxs, 15, 15, 16); 310 | } 311 | pm_ps->pm_flags |= PMF_DUCKED; 312 | pm_ps->viewheight = CROUCH_VIEWHEIGHT; 313 | return; 314 | } 315 | pm_ps->pm_flags &= ~PMF_INVULEXPAND; 316 | 317 | pm->mins[0] = -15; 318 | pm->mins[1] = -15; 319 | 320 | pm->maxs[0] = 15; 321 | pm->maxs[1] = 15; 322 | 323 | pm->mins[2] = MINS_Z; 324 | 325 | if (pm_ps->pm_type == PM_DEAD) 326 | { 327 | pm->maxs[2] = -8; 328 | pm_ps->viewheight = DEAD_VIEWHEIGHT; 329 | return; 330 | } 331 | 332 | if (pm->cmd.upmove < 0) 333 | { // duck 334 | pm_ps->pm_flags |= PMF_DUCKED; 335 | } 336 | else 337 | { // stand up if possible 338 | if (pm_ps->pm_flags & PMF_DUCKED) 339 | { 340 | // try to stand up 341 | pm->maxs[2] = 32; 342 | trap_CM_BoxTrace(&trace, pm_ps->origin, pm_ps->origin, pm->mins, pm->maxs, 0, pm->tracemask); 343 | if (!trace.allsolid) pm_ps->pm_flags &= ~PMF_DUCKED; 344 | } 345 | } 346 | 347 | if (pm_ps->pm_flags & PMF_DUCKED) 348 | { 349 | pm->maxs[2] = 16; 350 | pm_ps->viewheight = CROUCH_VIEWHEIGHT; 351 | } 352 | else 353 | { 354 | pm->maxs[2] = 32; 355 | pm_ps->viewheight = DEFAULT_VIEWHEIGHT; 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/cg_ammo.c: -------------------------------------------------------------------------------- 1 | #include "cg_ammo.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_draw.h" 5 | #include "cg_local.h" 6 | #include "cg_utils.h" 7 | #include "help.h" 8 | 9 | static vmCvar_t ammo; 10 | static vmCvar_t ammo_graph_xywh; 11 | static vmCvar_t ammo_text_xh; 12 | static vmCvar_t ammo_text_rgba; 13 | 14 | static cvarTable_t ammo_cvars[] = { 15 | { &ammo, "mdd_ammo", "0b0011", CVAR_ARCHIVE_ND }, 16 | { &ammo_graph_xywh, "mdd_ammo_graph_xywh", "610 100 24 24", CVAR_ARCHIVE_ND }, 17 | { &ammo_text_xh, "mdd_ammo_text_xh", "6 12", CVAR_ARCHIVE_ND }, 18 | { &ammo_text_rgba, "mdd_ammo_text_rgba", "1 1 1 1", CVAR_ARCHIVE_ND }, 19 | }; 20 | 21 | static help_t ammo_help[] = { 22 | { 23 | ammo_cvars + 0, 24 | BINARY_LITERAL, 25 | { 26 | "mdd_ammo 0bXXXX", 27 | " ||||", 28 | " |||+- draw hud", 29 | " ||+-- show weaponless ammo", 30 | " |+--- use gun icons", 31 | " +---- use 3D models", 32 | }, 33 | }, 34 | #define AMMO_DRAW 1 35 | #define AMMO_WEAPONLESSAMMO 2 36 | #define AMMO_GUN 4 37 | #define AMMO_3D 8 38 | { 39 | ammo_cvars + 1, 40 | X | Y | W | H, 41 | { 42 | "mdd_ammo_graph_xywh X X X X", 43 | }, 44 | }, 45 | { 46 | ammo_cvars + 2, 47 | X | H, 48 | { 49 | "mdd_ammo_text_xh X X", 50 | }, 51 | }, 52 | { 53 | ammo_cvars + 3, 54 | RGBA, 55 | { 56 | "mdd_ammo_text_rgba X X X X", 57 | }, 58 | }, 59 | }; 60 | 61 | typedef struct 62 | { 63 | qhandle_t graph_icons[16]; 64 | qhandle_t graph_models[16]; 65 | vec3_t graph_model_origin; 66 | vec3_t graph_model_angles; 67 | 68 | vec4_t graph_xywh; 69 | vec2_t text_xh; 70 | 71 | vec4_t text_rgba; 72 | } ammo_t; 73 | 74 | static ammo_t ammo_; 75 | 76 | void init_ammo(void) 77 | { 78 | init_cvars(ammo_cvars, ARRAY_LEN(ammo_cvars)); 79 | init_help(ammo_help, ARRAY_LEN(ammo_help)); 80 | 81 | ammo_.graph_icons[0] = trap_R_RegisterShader("icons/icona_machinegun"); 82 | ammo_.graph_icons[1] = trap_R_RegisterShader("icons/icona_shotgun"); 83 | ammo_.graph_icons[2] = trap_R_RegisterShader("icons/icona_grenade"); 84 | ammo_.graph_icons[3] = trap_R_RegisterShader("icons/icona_rocket"); 85 | ammo_.graph_icons[4] = trap_R_RegisterShader("icons/icona_lightning"); 86 | ammo_.graph_icons[5] = trap_R_RegisterShader("icons/icona_railgun"); 87 | ammo_.graph_icons[6] = trap_R_RegisterShader("icons/icona_plasma"); 88 | ammo_.graph_icons[7] = trap_R_RegisterShader("icons/icona_bfg"); 89 | ammo_.graph_icons[8] = trap_R_RegisterShader("icons/iconw_machinegun"); 90 | ammo_.graph_icons[9] = trap_R_RegisterShader("icons/iconw_shotgun"); 91 | ammo_.graph_icons[10] = trap_R_RegisterShader("icons/iconw_grenade"); 92 | ammo_.graph_icons[11] = trap_R_RegisterShader("icons/iconw_rocket"); 93 | ammo_.graph_icons[12] = trap_R_RegisterShader("icons/iconw_lightning"); 94 | ammo_.graph_icons[13] = trap_R_RegisterShader("icons/iconw_railgun"); 95 | ammo_.graph_icons[14] = trap_R_RegisterShader("icons/iconw_plasma"); 96 | ammo_.graph_icons[15] = trap_R_RegisterShader("icons/iconw_bfg"); 97 | 98 | ammo_.graph_models[0] = trap_R_RegisterModel("models/powerups/ammo/machinegunam.md3"); 99 | ammo_.graph_models[1] = trap_R_RegisterModel("models/powerups/ammo/shotgunam.md3"); 100 | ammo_.graph_models[2] = trap_R_RegisterModel("models/powerups/ammo/grenadeam.md3"); 101 | ammo_.graph_models[3] = trap_R_RegisterModel("models/powerups/ammo/rocketam.md3"); 102 | ammo_.graph_models[4] = trap_R_RegisterModel("models/powerups/ammo/lightningam.md3"); 103 | ammo_.graph_models[5] = trap_R_RegisterModel("models/powerups/ammo/railgunam.md3"); 104 | ammo_.graph_models[6] = trap_R_RegisterModel("models/powerups/ammo/plasmaam.md3"); 105 | ammo_.graph_models[7] = trap_R_RegisterModel("models/powerups/ammo/bfgam.md3"); 106 | ammo_.graph_models[8] = trap_R_RegisterModel("models/weapons2/machinegun/machinegun.md3"); 107 | ammo_.graph_models[9] = trap_R_RegisterModel("models/weapons2/shotgun/shotgun.md3"); 108 | ammo_.graph_models[10] = trap_R_RegisterModel("models/weapons2/grenadel/grenadel.md3"); 109 | ammo_.graph_models[11] = trap_R_RegisterModel("models/weapons2/rocketl/rocketl.md3"); 110 | ammo_.graph_models[12] = trap_R_RegisterModel("models/weapons2/lightning/lightning.md3"); 111 | ammo_.graph_models[13] = trap_R_RegisterModel("models/weapons2/railgun/railgun.md3"); 112 | ammo_.graph_models[14] = trap_R_RegisterModel("models/weapons2/plasma/plasma.md3"); 113 | ammo_.graph_models[15] = trap_R_RegisterModel("models/weapons2/bfg/bfg.md3"); 114 | 115 | memset(ammo_.graph_model_origin, 0, 3 * sizeof(vec_t)); 116 | ammo_.graph_model_origin[0] = 70.f; 117 | memset(ammo_.graph_model_angles, 0, 3 * sizeof(vec_t)); 118 | } 119 | 120 | void update_ammo(void) 121 | { 122 | update_cvars(ammo_cvars, ARRAY_LEN(ammo_cvars)); 123 | ammo.integer = cvar_getInteger("mdd_ammo"); 124 | } 125 | 126 | void draw_ammo(void) 127 | { 128 | if (!(ammo.integer & AMMO_DRAW)) return; 129 | 130 | ParseVec(ammo_graph_xywh.string, ammo_.graph_xywh, 4); 131 | ParseVec(ammo_text_rgba.string, ammo_.text_rgba, 4); 132 | 133 | float y = ammo_.graph_xywh[1]; 134 | playerState_t const* const ps = getPs(); 135 | for (uint8_t i = 0; i < 8; ++i) 136 | { 137 | int32_t const ammoLeft = ps->ammo[i + 2]; 138 | qboolean const hasWeapon = ps->stats[STAT_WEAPONS] & (1 << (i + 2)); 139 | 140 | if (!hasWeapon && (!(ammo.integer & AMMO_WEAPONLESSAMMO) || !ammoLeft)) continue; 141 | 142 | if (!(ammo.integer & AMMO_3D)) 143 | { 144 | CG_DrawPic( 145 | ammo_.graph_xywh[0], 146 | y, 147 | ammo_.graph_xywh[2], 148 | ammo_.graph_xywh[3], 149 | ammo_.graph_icons[i + (ammo.integer & AMMO_GUN ? 8 : 0)]); 150 | } 151 | else 152 | { 153 | ammo_.graph_model_angles[YAW] = 90.f + 20.f * sinf(getSnap()->serverTime / 1000.f); 154 | CG_Draw3DModel( 155 | ammo_.graph_xywh[0], 156 | y, 157 | ammo_.graph_xywh[2], 158 | ammo_.graph_xywh[3], 159 | ammo_.graph_models[i + (ammo.integer & AMMO_GUN ? 8 : 0)], 160 | 0, 161 | ammo_.graph_model_origin, 162 | ammo_.graph_model_angles); 163 | } 164 | 165 | if (!hasWeapon) // Mark weapon as unavailable 166 | { 167 | CG_DrawPic(ammo_.graph_xywh[0], y, ammo_.graph_xywh[2], ammo_.graph_xywh[3], cgs.media.deferShader); 168 | } 169 | 170 | ParseVec(ammo_text_xh.string, ammo_.text_xh, 2); 171 | qboolean const alignRight = ammo_.graph_xywh[0] + ammo_.graph_xywh[2] / 2.f > cgs.screenWidth / 2; 172 | CG_DrawText( 173 | alignRight ? ammo_.graph_xywh[0] - ammo_.text_xh[0] 174 | : ammo_.graph_xywh[0] + ammo_.graph_xywh[2] + ammo_.text_xh[0], 175 | y + ammo_.graph_xywh[3] / 2.f - .5f * ammo_.text_xh[1], 176 | ammo_.text_xh[1], 177 | vaf("%i", ammoLeft), 178 | ammo_.text_rgba, 179 | alignRight, 180 | qtrue /*shadow*/); 181 | y += ammo_.graph_xywh[3]; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/cg_consolecmds.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | 5 | This file is part of mdd client proxymod. 6 | 7 | mdd client proxymod is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | mdd client proxymod is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with mdd client proxymod. If not, see . 19 | ============================== 20 | Note: mdd client proxymod contains large quantities from the quake III arena source code 21 | */ 22 | #include "cg_local.h" 23 | #include "cg_vm.h" 24 | #include "help.h" 25 | 26 | #include 27 | 28 | static void cmdHelp(void); 29 | #ifndef NDEBUG 30 | static void cmdPointsTo_DebugOnly(void); 31 | #endif 32 | 33 | typedef struct 34 | { 35 | char const* cmd; 36 | void (*function)(void); 37 | } consoleCommand_t; 38 | 39 | static consoleCommand_t commands[] = { 40 | { "mdd_help", cmdHelp }, 41 | #ifndef NDEBUG 42 | { "mdd_points_to", cmdPointsTo_DebugOnly }, 43 | #endif 44 | }; 45 | 46 | /* 47 | ================= 48 | CG_ConsoleCommand 49 | 50 | The string has been tokenized and can be retrieved with 51 | Cmd_Argc() / Cmd_Argv() 52 | ================= 53 | */ 54 | qboolean CG_ConsoleCommand(void) 55 | { 56 | char cmd[MAX_STRING_CHARS]; 57 | trap_Argv(0, cmd, sizeof(cmd)); 58 | 59 | for (uint8_t i = 0; i < ARRAY_LEN(commands); ++i) 60 | { 61 | if (!Q_stricmp(cmd, commands[i].cmd)) 62 | { 63 | commands[i].function(); 64 | return qtrue; 65 | } 66 | } 67 | 68 | return qfalse; 69 | } 70 | 71 | /* 72 | ================= 73 | CG_InitConsoleCommands 74 | 75 | Let the client system know about all of our commands 76 | so it can perform tab completion 77 | ================= 78 | */ 79 | void CG_InitConsoleCommands(void) 80 | { 81 | for (uint8_t i = 0; i < ARRAY_LEN(commands); ++i) 82 | { 83 | trap_AddCommand(commands[i].cmd); 84 | } 85 | } 86 | 87 | static void cmdHelp(void) 88 | { 89 | if (trap_Argc() != 2) 90 | { 91 | trap_Print("usage: mdd_help \n"); 92 | cvars_with_help(); 93 | return; 94 | } 95 | 96 | char cvar[MAX_STRING_CHARS]; 97 | trap_Argv(1, cvar, sizeof(cvar)); 98 | 99 | cvar_help(cvar); 100 | } 101 | 102 | #ifndef NDEBUG 103 | static void cmdPointsTo_DebugOnly(void) 104 | { 105 | if (trap_Argc() != 2) 106 | { 107 | trap_Print("usage: mdd_points_to \n"); 108 | return; 109 | } 110 | 111 | char cmd[MAX_STRING_CHARS]; 112 | trap_Argv(1, cmd, sizeof(cmd)); 113 | 114 | long const offset = strtol(cmd, NULL, 0); 115 | trap_Print(vaf("%s -> 0x%lx\n", cmd, *(int32_t*)VM_ArgPtr(offset))); 116 | } 117 | #endif 118 | -------------------------------------------------------------------------------- /src/cg_cvar.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #include "cg_cvar.h" 22 | 23 | #include "cg_local.h" 24 | #include "q_assert.h" 25 | 26 | #include 27 | #include 28 | 29 | char const* ParseVec(char const* data, vec_t* vec, uint8_t size) 30 | { 31 | assert(data); 32 | assert(vec); 33 | ASSERT_GT(size, 0); 34 | 35 | char* end; 36 | for (uint8_t i = 0; i < size; ++i) 37 | { 38 | vec[i] = strtof(data, &end); 39 | assert(data != end); 40 | data = end; 41 | } 42 | return data; 43 | } 44 | 45 | char const* ParseVec4(char const* data, vec4_t* vec, uint8_t size) 46 | { 47 | assert(data); 48 | assert(vec); 49 | ASSERT_GT(size, 0); 50 | 51 | data = ParseVec(data, vec[0], ARRAY_LEN(*vec)); 52 | for (uint8_t i = 1; i < size; ++i) 53 | { 54 | while (*data++ != '/') assert(data[-1] != '\0'); 55 | data = ParseVec(data, vec[i], ARRAY_LEN(*vec)); 56 | } 57 | return data; 58 | } 59 | 60 | int32_t cvar_getInteger(char const* var_name) 61 | { 62 | char buffer[MAX_CVAR_VALUE_STRING]; 63 | trap_Cvar_VariableStringBuffer(var_name, buffer, sizeof(buffer)); 64 | char const* s = buffer; 65 | int8_t sign = 1; 66 | while (isspace(*s)) ++s; 67 | if (*s == '-') 68 | { 69 | sign = -1; 70 | ++s; 71 | } 72 | else if (*s == '+') 73 | { 74 | ++s; 75 | } 76 | if (*s == '0' && (s[1] == 'b' || s[1] == 'B')) 77 | { 78 | return sign * strtol(s + 2, NULL, 2); 79 | } 80 | return sign * strtol(s, NULL, 0); 81 | } 82 | 83 | float cvar_getValue(char const* var_name) 84 | { 85 | char buffer[MAX_CVAR_VALUE_STRING]; 86 | trap_Cvar_VariableStringBuffer(var_name, buffer, sizeof(buffer)); 87 | return strtof(buffer, NULL); 88 | } 89 | 90 | void init_cvars(cvarTable_t const* cvars, size_t size) 91 | { 92 | for (uint32_t i = 0; i < size; ++i) 93 | { 94 | trap_Cvar_Register(cvars[i].vmCvar, cvars[i].cvarName, cvars[i].defaultString, cvars[i].cvarFlags); 95 | } 96 | } 97 | 98 | void update_cvars(cvarTable_t const* cvars, size_t size) 99 | { 100 | for (uint32_t i = 0; i < size; ++i) 101 | { 102 | trap_Cvar_Update(cvars[i].vmCvar); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/cg_entity.c: -------------------------------------------------------------------------------- 1 | #include "cg_entity.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_utils.h" 5 | 6 | static vmCvar_t sound_local_only; 7 | 8 | static cvarTable_t sound_cvars[] = { 9 | { &sound_local_only, "mdd_sound_local_only", "0", CVAR_ARCHIVE_ND }, 10 | }; 11 | 12 | static entityState_t cg_entityStates[1024]; 13 | 14 | void init_entityStates(void) 15 | { 16 | init_cvars(sound_cvars, ARRAY_LEN(sound_cvars)); 17 | 18 | memset(cg_entityStates, -1, sizeof(cg_entityStates)); 19 | } 20 | 21 | void update_entityStates(void) 22 | { 23 | update_cvars(sound_cvars, ARRAY_LEN(sound_cvars)); 24 | 25 | snapshot_t const* const snap = getSnap(); 26 | for (int i = 0; i < snap->numEntities; i++) 27 | { 28 | cg_entityStates[snap->entities[i].number] = snap->entities[i]; 29 | } 30 | } 31 | 32 | int8_t should_filter_sound(int entity_num, int8_t is_loop) 33 | { 34 | // TODO: also bail if its sp? 35 | if (!sound_local_only.integer) return 0; 36 | 37 | // this messes up our own weapon noises 38 | // can't do anything about this w/o df code 39 | if (!is_loop && entity_num == ENTITYNUM_WORLD) return 1; 40 | 41 | // refers to a entity number we dont have (i.e. ourself!) 42 | // no clue whose sound it could be just let it play 43 | if (cg_entityStates[entity_num].number == -1) return 0; 44 | 45 | return cg_entityStates[entity_num].clientNum != getSnap()->ps.clientNum; 46 | } 47 | -------------------------------------------------------------------------------- /src/cg_gl.c: -------------------------------------------------------------------------------- 1 | #include "cg_gl.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_local.h" 5 | #include "cg_utils.h" 6 | #include "g_local.h" 7 | #include "help.h" 8 | #include "nade_tracking.h" 9 | 10 | static vmCvar_t gl_path_draw; 11 | static vmCvar_t gl_path_rgba; 12 | static vmCvar_t gl_path_preview_draw; 13 | static vmCvar_t gl_path_preview_rgba; 14 | 15 | static cvarTable_t gl_cvars[] = { 16 | { &gl_path_draw, "mdd_gl_path_draw", "1", CVAR_ARCHIVE_ND }, 17 | { &gl_path_rgba, "mdd_gl_path_rgba", "0 1 0 1", CVAR_ARCHIVE_ND }, 18 | { &gl_path_preview_draw, "mdd_gl_path_preview_draw", "1", CVAR_ARCHIVE_ND }, 19 | { &gl_path_preview_rgba, "mdd_gl_path_preview_rgba", "0 .5 0 1", CVAR_ARCHIVE_ND }, 20 | }; 21 | 22 | static help_t gl_help[] = { 23 | { 24 | gl_cvars + 1, 25 | RGBA, 26 | { 27 | "mdd_gl_path_rgba X X X X", 28 | }, 29 | }, 30 | { 31 | gl_cvars + 3, 32 | RGBA, 33 | { 34 | "mdd_gl_path_preview_rgba X X X X", 35 | }, 36 | }, 37 | }; 38 | 39 | static qhandle_t beam_shader; 40 | 41 | void init_gl(void) 42 | { 43 | init_cvars(gl_cvars, ARRAY_LEN(gl_cvars)); 44 | init_help(gl_help, ARRAY_LEN(gl_help)); 45 | 46 | beam_shader = trap_R_RegisterShader("railCore"); 47 | } 48 | 49 | void update_gl(void) 50 | { 51 | update_cvars(gl_cvars, ARRAY_LEN(gl_cvars)); 52 | } 53 | 54 | static void draw_nade_path(trajectory_t const* pos, int end_time, uint8_t const* color); 55 | 56 | void draw_gl(void) 57 | { 58 | uint8_t path_color[4]; 59 | uint8_t preview_color[4]; 60 | vec4_t color; 61 | 62 | playerState_t const* const ps = getPs(); 63 | 64 | snapshot_t const* const snap = getSnap(); 65 | 66 | if (ps->weapon == WP_GRENADE_LAUNCHER && gl_path_preview_draw.integer) 67 | { 68 | ParseVec(gl_path_preview_rgba.string, color, 4); 69 | for (uint8_t i = 0; i < 4; ++i) preview_color[i] = (uint8_t)(color[i] * 255); 70 | 71 | gentity_t ent; 72 | BG_PlayerStateToEntityState(ps, &ent.s, qtrue); 73 | // use the snapped origin for linking so it matches client predicted versions 74 | VectorCopy(ent.s.pos.trBase, ent.r.currentOrigin); 75 | 76 | // execute client events 77 | // ClientEvents 78 | gentity_t m; 79 | FireWeapon(ps, &m, &ent); 80 | 81 | draw_nade_path(&m.s.pos, cg.time + 2500, preview_color); 82 | } 83 | 84 | if (!gl_path_draw.integer) return; 85 | 86 | ParseVec(gl_path_rgba.string, color, 4); 87 | for (uint8_t i = 0; i < 4; ++i) path_color[i] = (uint8_t)(color[i] * 255); 88 | 89 | for (uint8_t i = 0; i < MAX_NADES; ++i) 90 | { 91 | if (nades[i].id >= 0 && nades[i].seen) 92 | { 93 | for (int j = 0; j < snap->numEntities; ++j) 94 | { 95 | entityState_t const* const entity = &snap->entities[j]; 96 | if (entity->number != nades[i].id) continue; 97 | draw_nade_path(&entity->pos, nades[i].explode_time, path_color); 98 | } 99 | } 100 | } 101 | } 102 | 103 | static void draw_nade_path(trajectory_t const* pos, int end_time, uint8_t const* color) 104 | { 105 | refEntity_t beam; 106 | trace_t trace; 107 | int sample_timer = 0; 108 | vec3_t currentOrigin, origin; 109 | 110 | if (pos->trType != TR_GRAVITY) return; 111 | 112 | memset(&beam, 0, sizeof(beam)); 113 | 114 | beam.reType = RT_RAIL_CORE; 115 | beam.customShader = beam_shader; 116 | 117 | AxisClear(beam.axis); 118 | memcpy(beam.shaderRGBA, color, sizeof(beam.shaderRGBA)); 119 | 120 | VectorCopy(pos->trBase, currentOrigin); 121 | if (cg.time > pos->trTime) 122 | BG_EvaluateTrajectory(pos, cg.time, beam.oldorigin); 123 | else 124 | VectorCopy(pos->trBase, beam.oldorigin); 125 | 126 | trajectory_t local_pos = *pos; 127 | for (int leveltime = local_pos.trTime + 8; leveltime < end_time; leveltime += 8) 128 | { 129 | BG_EvaluateTrajectory(&local_pos, leveltime, origin); 130 | trap_CM_BoxTrace(&trace, currentOrigin, origin, NULL, NULL, 0, MASK_SHOT); 131 | VectorCopy(trace.endpos, currentOrigin); 132 | 133 | sample_timer -= 8; 134 | if (sample_timer <= 0) 135 | { 136 | sample_timer = 32; 137 | VectorCopy(origin, beam.origin); 138 | if (leveltime >= cg.time) 139 | { 140 | vec3_t d, saved_origin; 141 | VectorCopy(beam.origin, saved_origin); 142 | VectorSubtract(beam.origin, beam.oldorigin, d); 143 | VectorMA(beam.oldorigin, .5f, d, beam.origin); 144 | trap_R_AddRefEntityToScene(&beam); 145 | VectorCopy(saved_origin, beam.oldorigin); 146 | } 147 | } 148 | 149 | if (trace.fraction != 1) 150 | { 151 | // G_BounceMissile 152 | vec3_t velocity; 153 | float dot; 154 | int hitTime; 155 | 156 | // reflect the velocity on the trace plane 157 | hitTime = (leveltime - 8) + (int)(8 * trace.fraction); 158 | BG_EvaluateTrajectoryDelta(&local_pos, hitTime, velocity); 159 | dot = DotProduct(velocity, trace.plane.normal); 160 | VectorMA(velocity, -2 * dot, trace.plane.normal, local_pos.trDelta); 161 | 162 | VectorScale(local_pos.trDelta, .65f, local_pos.trDelta); 163 | 164 | VectorAdd(currentOrigin, trace.plane.normal, currentOrigin); 165 | VectorCopy(currentOrigin, local_pos.trBase); 166 | local_pos.trTime = leveltime; 167 | 168 | sample_timer = 0; 169 | if (cg.time > local_pos.trTime) 170 | BG_EvaluateTrajectory(&local_pos, cg.time, beam.oldorigin); 171 | else 172 | VectorCopy(local_pos.trBase, beam.oldorigin); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/cg_hud.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #include "cg_hud.h" 22 | 23 | #include "bbox.h" 24 | #include "cg_ammo.h" 25 | #include "cg_cgaz.h" 26 | #include "cg_cvar.h" 27 | #include "cg_entity.h" 28 | #include "cg_gl.h" 29 | #include "cg_jump.h" 30 | #include "cg_local.h" 31 | #include "cg_rl.h" 32 | #include "cg_snap.h" 33 | #include "cg_timer.h" 34 | #include "compass.h" 35 | #include "help.h" 36 | #include "pitch.h" 37 | #include "version.h" 38 | 39 | static vmCvar_t hud; 40 | static vmCvar_t version; 41 | 42 | vmCvar_t mdd_fov; 43 | vmCvar_t mdd_projection; 44 | 45 | static cvarTable_t hud_cvars[] = { 46 | { &hud, "mdd_hud", "1", CVAR_ARCHIVE_ND }, 47 | { &version, "mdd_version", VERSION, CVAR_USERINFO | CVAR_INIT }, 48 | { &mdd_fov, "mdd_fov", "0", CVAR_ARCHIVE_ND }, 49 | { &mdd_projection, "mdd_projection", "0", CVAR_ARCHIVE_ND }, 50 | }; 51 | 52 | void init_hud(void) 53 | { 54 | init_cvars(hud_cvars, ARRAY_LEN(hud_cvars)); 55 | 56 | init_ammo(); 57 | init_bbox(); 58 | init_cgaz(); 59 | init_compass(); 60 | init_entityStates(); 61 | init_gl(); 62 | init_jump(); 63 | init_pitch(); 64 | init_rl(); 65 | init_snap(); 66 | init_timer(); 67 | } 68 | 69 | void del_hud(void) 70 | { 71 | del_help(); 72 | } 73 | 74 | void update_hud(void) 75 | { 76 | update_cvars(hud_cvars, ARRAY_LEN(hud_cvars)); 77 | 78 | if (!hud.integer) return; 79 | 80 | update_ammo(); 81 | update_bbox(); 82 | update_cgaz(); 83 | update_compass(); 84 | update_entityStates(); 85 | update_gl(); 86 | update_jump(); 87 | update_pitch(); 88 | update_rl(); 89 | update_snap(); 90 | update_timer(); 91 | } 92 | 93 | void draw_hud(void) 94 | { 95 | // First check if we have models, otherwise CM_ClipHandleToModel will fail 96 | if (!trap_CM_NumInlineModels()) return; 97 | 98 | if (!hud.integer) return; 99 | 100 | draw_compass(); 101 | draw_cgaz(); 102 | draw_snap(); 103 | draw_pitch(); 104 | 105 | draw_ammo(); 106 | draw_jump(); 107 | draw_timer(); 108 | } 109 | -------------------------------------------------------------------------------- /src/cg_jump.c: -------------------------------------------------------------------------------- 1 | #include "cg_jump.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_draw.h" 5 | #include "cg_local.h" 6 | #include "cg_utils.h" 7 | #include "help.h" 8 | 9 | #include 10 | 11 | static vmCvar_t jump; 12 | static vmCvar_t jump_maxDelay; 13 | static vmCvar_t jump_graph_xywh; 14 | static vmCvar_t jump_graph_rgba; 15 | static vmCvar_t jump_graph_rgbaOnGround; 16 | static vmCvar_t jump_graph_rgbaPreJump; 17 | static vmCvar_t jump_graph_rgbaPostJump; 18 | static vmCvar_t jump_graph_outline_w; 19 | static vmCvar_t jump_graph_outline_rgba; 20 | static vmCvar_t jump_text_xh; 21 | static vmCvar_t jump_text_rgba; 22 | 23 | static cvarTable_t jump_cvars[] = { 24 | { &jump, "mdd_jump", "3", CVAR_ARCHIVE_ND }, 25 | { &jump_maxDelay, "mdd_jump_maxDelay", "360", CVAR_ARCHIVE_ND }, 26 | { &jump_graph_xywh, "mdd_jump_graph_xywh", "8 8 8 104", CVAR_ARCHIVE_ND }, 27 | { &jump_graph_rgba, "mdd_jump_graph_rgba", ".5 .5 .5 .5", CVAR_ARCHIVE_ND }, 28 | { &jump_graph_rgbaOnGround, "mdd_jump_graph_rgbaOnGround", "0 1 0 .75", CVAR_ARCHIVE_ND }, 29 | { &jump_graph_rgbaPreJump, "mdd_jump_graph_rgbaPreJump", "0 0 1 .75", CVAR_ARCHIVE_ND }, 30 | { &jump_graph_rgbaPostJump, "mdd_jump_graph_rgbaPostJump", "1 0 0 .75", CVAR_ARCHIVE_ND }, 31 | { &jump_graph_outline_w, "mdd_jump_graph_outline_w", "1", CVAR_ARCHIVE_ND }, 32 | { &jump_graph_outline_rgba, "mdd_jump_graph_outline_rgba", "1 1 1 .75", CVAR_ARCHIVE_ND }, 33 | { &jump_text_xh, "mdd_jump_text_xh", "6 12", CVAR_ARCHIVE_ND }, 34 | { &jump_text_rgba, "mdd_jump_text_rgba", "1 1 1 1", CVAR_ARCHIVE_ND }, 35 | }; 36 | 37 | static help_t jump_help[] = { 38 | { 39 | jump_cvars + 2, 40 | X | Y | W | H, 41 | { 42 | "mdd_jump_graph_xywh X X X X", 43 | }, 44 | }, 45 | { 46 | jump_cvars + 3, 47 | RGBA, 48 | { 49 | "mdd_jump_graph_rgba X X X X", 50 | }, 51 | }, 52 | { 53 | jump_cvars + 4, 54 | RGBA, 55 | { 56 | "mdd_jump_graph_rgbaOnGround X X X X", 57 | }, 58 | }, 59 | { 60 | jump_cvars + 5, 61 | RGBA, 62 | { 63 | "mdd_jump_graph_rgbaPreJump X X X X", 64 | }, 65 | }, 66 | { 67 | jump_cvars + 6, 68 | RGBA, 69 | { 70 | "mdd_jump_graph_rgbaPostJump X X X X", 71 | }, 72 | }, 73 | { 74 | jump_cvars + 7, 75 | W, 76 | { 77 | "mdd_jump_graph_outline_w X", 78 | }, 79 | }, 80 | { 81 | jump_cvars + 8, 82 | RGBA, 83 | { 84 | "mdd_jump_graph_outline_rgba X X X X", 85 | }, 86 | }, 87 | { 88 | jump_cvars + 9, 89 | X | H, 90 | { 91 | "mdd_jump_text_xh X X", 92 | }, 93 | }, 94 | { 95 | jump_cvars + 10, 96 | RGBA, 97 | { 98 | "mdd_jump_text_rgba X X X X", 99 | }, 100 | }, 101 | }; 102 | 103 | void init_jump(void) 104 | { 105 | init_cvars(jump_cvars, ARRAY_LEN(jump_cvars)); 106 | init_help(jump_help, ARRAY_LEN(jump_help)); 107 | } 108 | 109 | void update_jump(void) 110 | { 111 | update_cvars(jump_cvars, ARRAY_LEN(jump_cvars)); 112 | } 113 | 114 | typedef enum 115 | { 116 | AIR_NOJUMP, 117 | AIR_JUMP, 118 | GROUND_JUMP, 119 | GROUND_NOJUMP, 120 | AIR_JUMPNORELEASE 121 | } state_t; 122 | 123 | typedef struct 124 | { 125 | // timestamps for computation 126 | uint32_t t_jumpPreGround; 127 | uint32_t t_groundTouch; 128 | 129 | // state machine 130 | state_t lastState; 131 | 132 | // draw data 133 | int32_t postDelay; 134 | int32_t preDelay; 135 | int32_t fullDelay; 136 | 137 | vec4_t graph_xywh; 138 | vec2_t text_xh; 139 | 140 | vec4_t graph_rgba; 141 | vec4_t graph_rgbaPostJump; 142 | vec4_t graph_rgbaOnGround; 143 | vec4_t graph_rgbaPreJump; 144 | vec4_t graph_outline_rgba; 145 | vec4_t text_rgba; 146 | } jump_t; 147 | 148 | static jump_t jump_; 149 | 150 | static void update_jump_state(void) 151 | { 152 | /* 153 | * To draw this hud we have to make a little state machine 154 | * 155 | * AIR_NOJUMP: The player is midair, not holding the jump button 156 | * AIR_JUMP: The player is midair, holding jump button 157 | * GROUND_JUMP: The player is on the ground, holding jump button 158 | * GROUND_NOJUMP: The player is on the ground, not holding jump button 159 | * AIR_JUMPNORELEASE: The player is midair, without releasing the jump button 160 | */ 161 | 162 | uint32_t const now = getSnap()->serverTime; 163 | playerState_t const* const ps = getPs(); 164 | int8_t const inAir = ps->groundEntityNum == ENTITYNUM_NONE; 165 | int8_t const jumping = (ps->stats[13] & PSF_USERINPUT_JUMP) / PSF_USERINPUT_JUMP; 166 | 167 | // Determine current state 168 | state_t state; 169 | switch (jump_.lastState) 170 | { 171 | case AIR_JUMP: 172 | case AIR_NOJUMP: 173 | if (inAir) 174 | { 175 | state = jumping ? AIR_JUMP : AIR_NOJUMP; 176 | } 177 | else 178 | { 179 | state = jumping ? GROUND_JUMP : GROUND_NOJUMP; 180 | } 181 | break; 182 | 183 | // Edge case at end of cycle 184 | case GROUND_NOJUMP: 185 | case GROUND_JUMP: 186 | case AIR_JUMPNORELEASE: 187 | if (inAir) 188 | { 189 | state = jumping ? AIR_JUMPNORELEASE : AIR_NOJUMP; 190 | } 191 | else 192 | { 193 | state = jumping ? GROUND_JUMP : GROUND_NOJUMP; 194 | } 195 | break; 196 | 197 | default: 198 | state = GROUND_NOJUMP; 199 | break; 200 | } 201 | 202 | // Act on current state 203 | switch (state) 204 | { 205 | case AIR_NOJUMP: // We spend the most time in this state that is why here we show the last jump stats 206 | if (jump_.lastState == GROUND_NOJUMP) 207 | { 208 | jump_.preDelay = jump_.t_jumpPreGround - now; 209 | jump_.postDelay = 0; 210 | jump_.fullDelay = 0; 211 | } 212 | else if (jump_.lastState == AIR_JUMP) 213 | { 214 | jump_.postDelay = 0; 215 | jump_.fullDelay = jump_.preDelay; 216 | } 217 | else if (jump_.lastState == AIR_JUMPNORELEASE) 218 | { 219 | jump_.fullDelay = jump_.postDelay; 220 | if (jump_.preDelay > 0) 221 | { 222 | jump_.fullDelay += jump_.preDelay; 223 | } 224 | } 225 | break; 226 | 227 | case AIR_JUMP: 228 | if (jump_.lastState == AIR_NOJUMP) jump_.t_jumpPreGround = now; 229 | jump_.preDelay = now - jump_.t_jumpPreGround; // ms 230 | break; 231 | 232 | case GROUND_JUMP: 233 | jump_.t_groundTouch = now; 234 | break; 235 | 236 | case GROUND_NOJUMP: 237 | if (jump_.lastState == AIR_JUMP || jump_.lastState == GROUND_JUMP) 238 | { 239 | jump_.postDelay = 0; 240 | jump_.fullDelay = jump_.preDelay; 241 | } 242 | else if (jump_.lastState == AIR_NOJUMP) 243 | { 244 | jump_.t_jumpPreGround = now; // groundtime 245 | } 246 | jump_.preDelay = jump_.t_jumpPreGround - now; 247 | jump_.t_groundTouch = now; 248 | break; 249 | 250 | case AIR_JUMPNORELEASE: 251 | if (jump_.lastState == GROUND_NOJUMP) 252 | { 253 | jump_.preDelay = jump_.t_jumpPreGround - now; 254 | } 255 | jump_.postDelay = now - jump_.t_groundTouch; // ms 256 | break; 257 | 258 | default: 259 | break; 260 | } 261 | 262 | if (jump_.preDelay > jump_maxDelay.integer) jump_.preDelay = jump_maxDelay.integer; 263 | if (jump_.preDelay < -jump_maxDelay.integer) jump_.preDelay = -jump_maxDelay.integer; 264 | if (jump_.postDelay > jump_maxDelay.integer) jump_.postDelay = jump_maxDelay.integer; 265 | 266 | // if (state != lastState) 267 | // g_syscall( CG_PRINT, vaf("%u %u\n", state, lastState)); 268 | jump_.lastState = state; 269 | } 270 | 271 | void draw_jump(void) 272 | { 273 | if (!jump.integer) return; 274 | 275 | update_jump_state(); 276 | 277 | ParseVec(jump_text_rgba.string, jump_.text_rgba, 4); 278 | ParseVec(jump_graph_rgba.string, jump_.graph_rgba, 4); 279 | ParseVec(jump_graph_rgbaOnGround.string, jump_.graph_rgbaOnGround, 4); 280 | ParseVec(jump_graph_rgbaPreJump.string, jump_.graph_rgbaPreJump, 4); 281 | ParseVec(jump_graph_rgbaPostJump.string, jump_.graph_rgbaPostJump, 4); 282 | ParseVec(jump_graph_outline_rgba.string, jump_.graph_outline_rgba, 4); 283 | 284 | ParseVec(jump_graph_xywh.string, jump_.graph_xywh, 4); 285 | float const graph_hh = jump_.graph_xywh[3] / 2.f; // half height 286 | float const graph_m = jump_.graph_xywh[1] + graph_hh; 287 | 288 | float const upHeight = ((float)jump_.postDelay / (float)jump_maxDelay.integer) * graph_hh; 289 | float const downHeight = ((float)abs(jump_.preDelay) / (float)jump_maxDelay.integer) * graph_hh; 290 | 291 | if (jump.integer & 1) 292 | { 293 | CG_FillRect(jump_.graph_xywh[0], jump_.graph_xywh[1], jump_.graph_xywh[2], jump_.graph_xywh[3], jump_.graph_rgba); 294 | CG_FillRect( 295 | jump_.graph_xywh[0], 296 | graph_m, 297 | jump_.graph_xywh[2], 298 | downHeight, 299 | jump_.preDelay < 0 ? jump_.graph_rgbaOnGround : jump_.graph_rgbaPreJump); 300 | CG_FillRect(jump_.graph_xywh[0], graph_m - upHeight, jump_.graph_xywh[2], upHeight, jump_.graph_rgbaPostJump); 301 | CG_DrawRect( 302 | jump_.graph_xywh[0], 303 | jump_.graph_xywh[1], 304 | jump_.graph_xywh[2], 305 | jump_.graph_xywh[3], 306 | jump_graph_outline_w.value, 307 | jump_.graph_outline_rgba); 308 | } 309 | if (jump.integer & 2) 310 | { 311 | ParseVec(jump_text_xh.string, jump_.text_xh, 2); 312 | qboolean const alignRight = jump_.graph_xywh[0] + jump_.graph_xywh[2] / 2.f > cgs.screenWidth / 2; 313 | CG_DrawText( 314 | alignRight ? jump_.graph_xywh[0] - jump_.text_xh[0] 315 | : jump_.graph_xywh[0] + jump_.graph_xywh[2] + jump_.text_xh[0], 316 | graph_m - 1.5f * jump_.text_xh[1], 317 | jump_.text_xh[1], 318 | vaf("%i", jump_.postDelay), 319 | jump_.text_rgba, 320 | alignRight, 321 | qtrue /*shadow*/); 322 | CG_DrawText( 323 | alignRight ? jump_.graph_xywh[0] - jump_.text_xh[0] 324 | : jump_.graph_xywh[0] + jump_.graph_xywh[2] + jump_.text_xh[0], 325 | graph_m - .5f * jump_.text_xh[1], 326 | jump_.text_xh[1], 327 | vaf("%i", jump_.fullDelay), 328 | jump_.text_rgba, 329 | alignRight, 330 | qtrue /*shadow*/); 331 | CG_DrawText( 332 | alignRight ? jump_.graph_xywh[0] - jump_.text_xh[0] 333 | : jump_.graph_xywh[0] + jump_.graph_xywh[2] + jump_.text_xh[0], 334 | graph_m + .5f * jump_.text_xh[1], 335 | jump_.text_xh[1], 336 | vaf("%i", jump_.preDelay), 337 | jump_.text_rgba, 338 | alignRight, 339 | qtrue /*shadow*/); 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/cg_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #include "cg_main.h" 22 | 23 | #include "cg_hud.h" 24 | #include "q_assert.h" 25 | #include "version.h" 26 | 27 | #include 28 | 29 | static void CG_Init(int32_t clientNum); 30 | 31 | static void CG_Shutdown(void); 32 | 33 | /* CLIENT to VM */ 34 | intptr_t vmMain( 35 | int32_t cmd, 36 | int32_t arg0, 37 | int32_t arg1, 38 | int32_t arg2, 39 | int32_t arg3, 40 | int32_t arg4, 41 | int32_t arg5, 42 | int32_t arg6, 43 | int32_t arg7, 44 | int32_t arg8, 45 | int32_t arg9, 46 | int32_t arg10, 47 | int32_t arg11) 48 | { 49 | intptr_t ret; 50 | 51 | /* PRE CALL */ 52 | switch (cmd) 53 | { 54 | case CG_INIT: // void CG_Init( int32_t serverMessageNum, int32_t serverCommandSequence, int32_t clientNum ) 55 | CG_Init(arg2); 56 | break; 57 | 58 | case CG_CONSOLE_COMMAND: // qboolean (*CG_ConsoleCommand)( void ); 59 | break; 60 | 61 | case CG_DRAW_ACTIVE_FRAME: // void (*CG_DrawActiveFrame)( int32_t serverTime, stereoFrame_t stereoView, qboolean 62 | // demoPlayback ); 63 | CG_DrawActiveFrame(arg0, arg1, arg2); 64 | break; 65 | 66 | case CG_CROSSHAIR_PLAYER: // int32_t (*CG_CrosshairPlayer)( void ); 67 | break; 68 | 69 | case CG_LAST_ATTACKER: // int32_t (*CG_LastAttacker)( void ); 70 | break; 71 | 72 | case CG_KEY_EVENT: // void (*CG_KeyEvent)( int32_t key, qboolean down ); 73 | break; 74 | 75 | case CG_MOUSE_EVENT: // void (*CG_MouseEvent)( int32_t dx, int32_t dy ); 76 | break; 77 | 78 | case CG_EVENT_HANDLING: // void (*CG_EventHandling)(int32_t type); 79 | break; 80 | 81 | case CG_SHUTDOWN: // void (*CG_Shutdown)( void ); 82 | break; 83 | 84 | case -1: 85 | setVMPtr(arg0); 86 | return 0; 87 | break; 88 | } 89 | 90 | /* call vmMain() in the VM (defrag) */ 91 | ret = callVM(cmd, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); 92 | 93 | /* POST CALL */ 94 | switch (cmd) 95 | { 96 | case CG_INIT: // void CG_Init( int32_t serverMessageNum, int32_t serverCommandSequence, int32_t clientNum ) 97 | break; 98 | 99 | case CG_CONSOLE_COMMAND: // qboolean (*CG_ConsoleCommand)( void ); 100 | if (!ret) ret = CG_ConsoleCommand(); 101 | break; 102 | 103 | case CG_DRAW_ACTIVE_FRAME: // void (*CG_DrawActiveFrame)( int32_t serverTime, stereoFrame_t stereoView, qboolean 104 | // demoPlayback ) 105 | break; 106 | 107 | case CG_CROSSHAIR_PLAYER: // int32_t (*CG_CrosshairPlayer)( void ) 108 | break; 109 | 110 | case CG_LAST_ATTACKER: // int32_t (*CG_LastAttacker)( void ); 111 | break; 112 | 113 | case CG_KEY_EVENT: // void (*CG_KeyEvent)( int32_t key, qboolean down ) 114 | break; 115 | 116 | case CG_MOUSE_EVENT: // void (*CG_MouseEvent)( int32_t dx, int32_t dy ) 117 | break; 118 | 119 | case CG_EVENT_HANDLING: // void (*CG_EventHandling)(int32_t type) 120 | break; 121 | 122 | case CG_SHUTDOWN: // void (*CG_Shutdown)( void ) 123 | CG_Shutdown(); 124 | ASSERT_EQ(ret, 0); 125 | break; 126 | } 127 | 128 | return ret; 129 | } 130 | 131 | cg_t cg; 132 | cgs_t cgs; 133 | 134 | /* 135 | ================= 136 | CG_RegisterCvars 137 | ================= 138 | */ 139 | static void CG_RegisterCvars(void) 140 | { 141 | init_hud(); 142 | } 143 | 144 | /* 145 | ================= 146 | CG_UpdateCvars 147 | ================= 148 | */ 149 | void CG_UpdateCvars(void) 150 | { 151 | update_hud(); 152 | } 153 | 154 | //=========================================================================== 155 | 156 | /* 157 | ================= 158 | CG_ConfigString 159 | ================= 160 | */ 161 | char const* CG_ConfigString(int32_t index) 162 | { 163 | if (index < 0 || index >= MAX_CONFIGSTRINGS) 164 | { 165 | trap_Error(vaf("CG_ConfigString: bad index: %i", index)); 166 | } 167 | return cgs.gameState.stringData + cgs.gameState.stringOffsets[index]; 168 | } 169 | 170 | //================================================================== 171 | 172 | /* 173 | ================= 174 | CG_Init 175 | 176 | Called after every level change or subsystem restart 177 | Will perform callbacks to make the loading info screen update. 178 | ================= 179 | */ 180 | static void CG_Init(int32_t clientNum) 181 | { 182 | trap_Print(vaf("^7[^1m^3D^1d^7] cgame-proxy %s\n", VERSION)); 183 | 184 | // clear everything 185 | memset(&cgs, 0, sizeof(cgs)); 186 | memset(&cg, 0, sizeof(cg)); 187 | 188 | cg.clientNum = clientNum; 189 | 190 | // load a few needed things before we do any screen updates 191 | cgs.media.charsetShader = trap_R_RegisterShader("gfx/2d/bigchars"); 192 | cgs.media.whiteShader = trap_R_RegisterShader("white"); 193 | cgs.media.charsetProp = trap_R_RegisterShaderNoMip("menu/art/font1_prop.tga"); 194 | cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip("menu/art/font1_prop_glo.tga"); 195 | cgs.media.charsetPropB = trap_R_RegisterShaderNoMip("menu/art/font2_prop.tga"); 196 | 197 | CG_RegisterCvars(); 198 | 199 | CG_InitConsoleCommands(); 200 | 201 | // get the rendering configuration from the client system 202 | trap_GetGlconfig(&cgs.glconfig); // rendering configuration 203 | cgs.screenXScale = cgs.glconfig.vidWidth / (float)SCREEN_WIDTH; 204 | cgs.screenWidth = SCREEN_WIDTH; // = cgs.glconfig.vidWidth / cgs.screenXScale) 205 | cgs.screenHeight = cgs.glconfig.vidHeight / cgs.screenXScale; 206 | 207 | // get the gamestate from the client system 208 | trap_GetGameState(&cgs.gameState); 209 | 210 | cgs.levelStartTime = atoi(CG_ConfigString(CS_LEVEL_START_TIME)); 211 | 212 | // CG_RegisterGraphics 213 | cgs.media.deferShader = trap_R_RegisterShaderNoMip("gfx/2d/defer"); 214 | 215 | initVM(); 216 | } 217 | 218 | /* 219 | ================= 220 | CG_Shutdown 221 | 222 | Called before every level change or subsystem restart 223 | ================= 224 | */ 225 | void CG_Shutdown(void) 226 | { 227 | // some mods may need to do cleanup work here, 228 | // like closing files or archiving session data 229 | 230 | del_hud(); 231 | 232 | intptr_t const ret = callVM_Destroy(); 233 | (void)ret; 234 | ASSERT_EQ(ret, 0); 235 | } 236 | -------------------------------------------------------------------------------- /src/cg_marks.c: -------------------------------------------------------------------------------- 1 | #include "cg_local.h" 2 | #include "q_assert.h" 3 | 4 | /* 5 | ================= 6 | CG_ImpactMark 7 | 8 | origin should be a point within a unit of the plane 9 | dir should be the plane normal 10 | 11 | temporary marks will not be stored or randomly oriented, but immediately 12 | passed to the renderer. 13 | ================= 14 | */ 15 | #define MAX_MARK_FRAGMENTS 128 16 | #define MAX_MARK_POINTS 384 17 | 18 | void CG_ImpactMark( 19 | qhandle_t markShader, 20 | vec3_t const origin, 21 | vec3_t const dir, 22 | float orientation, 23 | float red, 24 | float green, 25 | float blue, 26 | float alpha, 27 | qboolean alphaFade, 28 | float radius, 29 | qboolean temporary) 30 | { 31 | (void)alphaFade; 32 | vec3_t axis[3]; 33 | float texCoordScale; 34 | vec3_t originalPoints[4]; 35 | byte colors[4]; 36 | int32_t i, j; 37 | int32_t numFragments; 38 | markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; 39 | vec3_t markPoints[MAX_MARK_POINTS]; 40 | vec3_t projection; 41 | 42 | ASSERT_TRUE(temporary); 43 | 44 | // if (!cg_addMarks.integer) 45 | // { 46 | // return; 47 | // } 48 | 49 | if (radius <= 0) 50 | { 51 | trap_Error("CG_ImpactMark called with <= 0 radius"); 52 | } 53 | 54 | // if ( markTotal >= MAX_MARK_POLYS ) { 55 | // return; 56 | //} 57 | 58 | // create the texture axis 59 | VectorNormalize2(dir, axis[0]); 60 | PerpendicularVector(axis[1], axis[0]); 61 | RotatePointAroundVector(axis[2], axis[0], axis[1], orientation); 62 | CrossProduct(axis[0], axis[2], axis[1]); 63 | 64 | texCoordScale = .5f * 1.f / radius; 65 | 66 | // create the full polygon 67 | for (i = 0; i < 3; i++) 68 | { 69 | originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; 70 | originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; 71 | originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; 72 | originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; 73 | } 74 | 75 | // get the fragments 76 | VectorScale(dir, -20, projection); 77 | numFragments = trap_CM_MarkFragments( 78 | 4, (void*)originalPoints, projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments); 79 | 80 | colors[0] = (byte)(red * 255); 81 | colors[1] = (byte)(green * 255); 82 | colors[2] = (byte)(blue * 255); 83 | colors[3] = (byte)(alpha * 255); 84 | 85 | for (i = 0, mf = markFragments; i < numFragments; i++, mf++) 86 | { 87 | polyVert_t* v; 88 | polyVert_t verts[MAX_VERTS_ON_POLY]; 89 | // markPoly_t* mark; 90 | 91 | // we have an upper limit on the complexity of polygons 92 | // that we store persistantly 93 | if (mf->numPoints > MAX_VERTS_ON_POLY) 94 | { 95 | mf->numPoints = MAX_VERTS_ON_POLY; 96 | } 97 | for (j = 0, v = verts; j < mf->numPoints; j++, v++) 98 | { 99 | vec3_t delta; 100 | 101 | VectorCopy(markPoints[mf->firstPoint + j], v->xyz); 102 | 103 | VectorSubtract(v->xyz, origin, delta); 104 | v->st[0] = .5f + DotProduct(delta, axis[1]) * texCoordScale; 105 | v->st[1] = .5f + DotProduct(delta, axis[2]) * texCoordScale; 106 | memcpy(v->modulate, colors, sizeof(v->modulate)); 107 | } 108 | 109 | // if it is a temporary (shadow) mark, add it immediately and forget about it 110 | if (temporary) 111 | { 112 | trap_R_AddPolyToScene(markShader, mf->numPoints, verts); 113 | continue; 114 | } 115 | 116 | // // otherwise save it persistantly 117 | // mark = CG_AllocMark(); 118 | // mark->time = cg.time; 119 | // mark->alphaFade = alphaFade; 120 | // mark->markShader = markShader; 121 | // mark->poly.numVerts = mf->numPoints; 122 | // mark->color[0] = red; 123 | // mark->color[1] = green; 124 | // mark->color[2] = blue; 125 | // mark->color[3] = alpha; 126 | // memcpy(mark->verts, verts, mf->numPoints * sizeof(verts[0])); 127 | // markTotal++; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/cg_rl.c: -------------------------------------------------------------------------------- 1 | #include "cg_rl.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_local.h" 5 | #include "cg_utils.h" 6 | #include "g_local.h" 7 | #include "help.h" 8 | 9 | #define MAX_RL_TIME 15000 10 | 11 | static vmCvar_t target_draw; 12 | static vmCvar_t target_shader; 13 | static vmCvar_t target_size; 14 | static vmCvar_t path_draw; 15 | static vmCvar_t path_rgba; 16 | 17 | static cvarTable_t rl_cvars[] = { 18 | { &target_draw, "mdd_rl_target_draw", "0", CVAR_ARCHIVE_ND }, 19 | { &target_shader, "mdd_rl_target_shader", "rlTraceMark", CVAR_ARCHIVE_ND }, 20 | { &target_size, "mdd_rl_target_size", "24", CVAR_ARCHIVE_ND }, 21 | { &path_draw, "mdd_rl_path_draw", "0", CVAR_ARCHIVE_ND }, 22 | { &path_rgba, "mdd_rl_path_rgba", "1 0 0 0", CVAR_ARCHIVE_ND }, 23 | }; 24 | 25 | static help_t rl_help[] = { 26 | { 27 | rl_cvars + 4, 28 | RGBA, 29 | { 30 | "mdd_rl_path_rgba X X X X", 31 | }, 32 | }, 33 | }; 34 | 35 | typedef struct 36 | { 37 | qhandle_t line_shader; 38 | } rl_t; 39 | 40 | static rl_t rl_; 41 | 42 | void init_rl(void) 43 | { 44 | init_cvars(rl_cvars, ARRAY_LEN(rl_cvars)); 45 | init_help(rl_help, ARRAY_LEN(rl_help)); 46 | 47 | rl_.line_shader = trap_R_RegisterShader("railCore"); 48 | } 49 | 50 | void update_rl(void) 51 | { 52 | update_cvars(rl_cvars, ARRAY_LEN(rl_cvars)); 53 | } 54 | 55 | void draw_rl(void) 56 | { 57 | if (!target_draw.integer && !path_draw.integer) return; 58 | 59 | refEntity_t beam; 60 | trace_t beam_trace; 61 | vec3_t origin; 62 | vec3_t dest; 63 | 64 | snapshot_t const* const snap = getSnap(); 65 | playerState_t const* const ps = getPs(); 66 | 67 | if (target_draw.integer && ps->weapon == WP_ROCKET_LAUNCHER) 68 | { 69 | gentity_t ent; 70 | BG_PlayerStateToEntityState(ps, &ent.s, qtrue); 71 | // use the snapped origin for linking so it matches client predicted versions 72 | VectorCopy(ent.s.pos.trBase, ent.r.currentOrigin); 73 | 74 | // execute client events 75 | // ClientEvents 76 | gentity_t m; 77 | FireWeapon(ps, &m, &ent); 78 | 79 | BG_EvaluateTrajectory(&m.s.pos, cg.time, origin); 80 | BG_EvaluateTrajectory(&m.s.pos, m.s.pos.trTime + MAX_RL_TIME, dest); 81 | trap_CM_BoxTrace(&beam_trace, origin, dest, NULL, NULL, 0, CONTENTS_SOLID); 82 | qhandle_t m_shader = trap_R_RegisterShader(target_shader.string); 83 | CG_ImpactMark( 84 | m_shader, beam_trace.endpos, beam_trace.plane.normal, 0, 1, 1, 1, 1, qfalse, target_size.value, qtrue); 85 | } 86 | 87 | // TODO: lerp trajectory stuff? 88 | for (int32_t i = 0; i < snap->numEntities; ++i) 89 | { 90 | entityState_t const entity = snap->entities[i]; 91 | if (entity.eType == ET_MISSILE && entity.weapon == WP_ROCKET_LAUNCHER && entity.clientNum == ps->clientNum) 92 | { 93 | BG_EvaluateTrajectory(&entity.pos, cg.time, origin); 94 | BG_EvaluateTrajectory(&entity.pos, entity.pos.trTime + MAX_RL_TIME, dest); 95 | trap_CM_BoxTrace(&beam_trace, origin, dest, NULL, NULL, 0, CONTENTS_SOLID); 96 | if (path_draw.integer) 97 | { 98 | vec4_t color; 99 | ParseVec(path_rgba.string, color, 4); 100 | 101 | memset(&beam, 0, sizeof(beam)); 102 | VectorCopy(origin, beam.oldorigin); 103 | VectorCopy(beam_trace.endpos, beam.origin); 104 | beam.reType = RT_RAIL_CORE; 105 | beam.customShader = rl_.line_shader; 106 | AxisClear(beam.axis); 107 | beam.shaderRGBA[0] = (byte)(color[0] * 255); 108 | beam.shaderRGBA[1] = (byte)(color[1] * 255); 109 | beam.shaderRGBA[2] = (byte)(color[2] * 255); 110 | beam.shaderRGBA[3] = (byte)(color[3] * 255); 111 | trap_R_AddRefEntityToScene(&beam); 112 | } 113 | 114 | if (target_draw.integer) 115 | { 116 | qhandle_t m_shader = trap_R_RegisterShader(target_shader.string); 117 | CG_ImpactMark( 118 | m_shader, beam_trace.endpos, beam_trace.plane.normal, 0, 1, 1, 1, 1, qfalse, target_size.value, qtrue); 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/cg_timer.c: -------------------------------------------------------------------------------- 1 | #include "cg_timer.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_draw.h" 5 | #include "cg_local.h" 6 | #include "cg_utils.h" 7 | #include "help.h" 8 | #include "nade_tracking.h" 9 | 10 | #define MAX_GB_TIME 250 11 | #define MAX_RL_TIME 15000 12 | 13 | #define NADE_EXPLODE_TIME 2500 14 | 15 | nade_info_t nades[MAX_NADES]; 16 | 17 | static vmCvar_t timer; 18 | static vmCvar_t timer_xywh; 19 | static vmCvar_t timer_item_w; 20 | static vmCvar_t timer_item_rgba; 21 | static vmCvar_t timer_gb_rgba; 22 | static vmCvar_t timer_outline_w; 23 | static vmCvar_t timer_outline_rgba; 24 | 25 | static cvarTable_t timer_cvars[] = { 26 | { &timer, "mdd_timer", "0", CVAR_ARCHIVE_ND }, 27 | { &timer_xywh, "mdd_timer_xywh", "275 275 100 16", CVAR_ARCHIVE_ND }, 28 | { &timer_item_w, "mdd_timer_item_w", "3", CVAR_ARCHIVE_ND }, 29 | { &timer_item_rgba, "mdd_timer_item_rgba", "1 1 0 1", CVAR_ARCHIVE_ND }, 30 | { &timer_gb_rgba, "mdd_timer_gb_rgba", "1 0 0 1", CVAR_ARCHIVE_ND }, 31 | { &timer_outline_w, "mdd_timer_outline_w", "1", CVAR_ARCHIVE_ND }, 32 | { &timer_outline_rgba, "mdd_timer_outline_rgba", "1 1 1 1", CVAR_ARCHIVE_ND }, 33 | }; 34 | 35 | static help_t timer_help[] = { 36 | { 37 | timer_cvars + 1, 38 | X | Y | W | H, 39 | { 40 | "mdd_timer_xywh X X X X", 41 | }, 42 | }, 43 | { 44 | timer_cvars + 2, 45 | W, 46 | { 47 | "mdd_timer_item_w X", 48 | }, 49 | }, 50 | { 51 | timer_cvars + 3, 52 | RGBA, 53 | { 54 | "mdd_timer_item_rgba X X X X", 55 | }, 56 | }, 57 | { 58 | timer_cvars + 4, 59 | RGBA, 60 | { 61 | "mdd_timer_gb_rgba X X X X", 62 | }, 63 | }, 64 | { 65 | timer_cvars + 5, 66 | W, 67 | { 68 | "mdd_timer_outline_w X", 69 | }, 70 | }, 71 | { 72 | timer_cvars + 6, 73 | RGBA, 74 | { 75 | "mdd_timer_outline_rgba X X X X", 76 | }, 77 | }, 78 | }; 79 | 80 | void init_timer(void) 81 | { 82 | init_cvars(timer_cvars, ARRAY_LEN(timer_cvars)); 83 | init_help(timer_help, ARRAY_LEN(timer_help)); 84 | 85 | for (uint8_t i = 0; i < MAX_NADES; ++i) nades[i].id = -1; 86 | } 87 | 88 | void update_timer(void) 89 | { 90 | update_cvars(timer_cvars, ARRAY_LEN(timer_cvars)); 91 | } 92 | 93 | typedef struct 94 | { 95 | vec4_t graph_xywh; 96 | 97 | vec4_t graph_outline_rgba; 98 | vec4_t graph_item_rgba; 99 | vec4_t graph_gb_rgba; 100 | } timer_t; 101 | 102 | static timer_t timer_; 103 | 104 | // XPC32: Also some of the quadratic loops can be simplified in the nade timer and gl trace code 105 | // Because of entity state tracking 106 | // And I should prolly be memsetting ent states array to 0 and setting just number or cn to -1 or something 107 | 108 | static int find_nade(int nade_id); 109 | static int track_nade(int nade_id, int time); 110 | static void draw_item(float progress, vec4_t const color); 111 | 112 | void draw_timer(void) 113 | { 114 | if (!timer.integer) return; 115 | 116 | ParseVec(timer_xywh.string, timer_.graph_xywh, 4); 117 | 118 | ParseVec(timer_outline_rgba.string, timer_.graph_outline_rgba, 4); 119 | ParseVec(timer_item_rgba.string, timer_.graph_item_rgba, 4); 120 | ParseVec(timer_gb_rgba.string, timer_.graph_gb_rgba, 4); 121 | 122 | // draw the outline 123 | CG_DrawRect( 124 | timer_.graph_xywh[0], 125 | timer_.graph_xywh[1], 126 | timer_.graph_xywh[2], 127 | timer_.graph_xywh[3], 128 | timer_outline_w.value, 129 | timer_.graph_outline_rgba); 130 | 131 | snapshot_t const* const snap = getSnap(); 132 | playerState_t const* const ps = getPs(); 133 | 134 | // gb stuff 135 | // TODO: make gb timer off-able and use pps if available and cvar 136 | if (ps->pm_flags & PMF_TIME_KNOCKBACK && ps->groundEntityNum != ENTITYNUM_NONE && !(ps->pm_flags & PMF_RESPAWNED)) 137 | { 138 | draw_item(1.f - (float)ps->pm_time / MAX_GB_TIME, timer_.graph_gb_rgba); 139 | } 140 | 141 | // cull exploded nades to make space 142 | // and set valid nades to not seen in snapshot yet 143 | for (int i = 0; i < MAX_NADES; ++i) 144 | { 145 | if (nades[i].id == -1) continue; 146 | 147 | if (nades[i].explode_time - snap->serverTime <= 0) 148 | nades[i].id = -1; 149 | else 150 | nades[i].seen = 0; 151 | } 152 | 153 | // traverse ent list to update nade infos 154 | for (int i = 0; i < snap->numEntities; i++) 155 | { 156 | entityState_t entity = snap->entities[i]; 157 | if (entity.eType == ET_MISSILE && entity.weapon == WP_GRENADE_LAUNCHER && entity.clientNum == ps->clientNum) 158 | { 159 | int const nade_index = find_nade(entity.number); 160 | if (nade_index == -1) // new nade 161 | track_nade(entity.number, snap->serverTime); 162 | else 163 | nades[nade_index].seen = 1; 164 | } 165 | else if (entity.eType == ET_MISSILE && entity.weapon == WP_ROCKET_LAUNCHER && entity.clientNum == ps->clientNum) 166 | { 167 | trace_t t; 168 | vec3_t origin; 169 | vec3_t dest; 170 | float const elapsed_time = (cg.time - entity.pos.trTime) * .001f; 171 | 172 | VectorMA(entity.pos.trBase, elapsed_time, entity.pos.trDelta, origin); 173 | VectorMA(entity.pos.trBase, MAX_RL_TIME * .001f, entity.pos.trDelta, dest); 174 | 175 | // a rocket dest should never change (ignoring movers) 176 | // trace doesn't need to be recomputed each time 177 | trap_CM_BoxTrace(&t, origin, dest, NULL, NULL, 0, CONTENTS_SOLID); 178 | float total_time = Distance(entity.pos.trBase, t.endpos) / VectorLength(entity.pos.trDelta); 179 | draw_item(elapsed_time / total_time, timer_.graph_item_rgba); 180 | } 181 | } 182 | 183 | // cull nades not in snapshot (prolly prematurely detonated) and draw the rest 184 | for (int i = 0; i < MAX_NADES; i++) 185 | { 186 | if (nades[i].id == -1) continue; 187 | 188 | if (!nades[i].seen) 189 | { 190 | nades[i].id = -1; 191 | } 192 | else 193 | { 194 | float progress = 1.f - (float)(nades[i].explode_time - snap->serverTime) / NADE_EXPLODE_TIME; 195 | draw_item(progress, timer_.graph_item_rgba); 196 | } 197 | } 198 | } 199 | 200 | static int find_nade(int nade_id) 201 | { 202 | for (uint8_t i = 0; i < MAX_NADES; ++i) 203 | { 204 | if (nades[i].id == nade_id) return i; 205 | } 206 | 207 | return -1; 208 | } 209 | 210 | static int track_nade(int nade_id, int time) 211 | { 212 | for (uint8_t i = 0; i < MAX_NADES; ++i) 213 | { 214 | if (nades[i].id == -1) 215 | { 216 | nades[i].id = nade_id; 217 | nades[i].explode_time = time + NADE_EXPLODE_TIME; 218 | nades[i].seen = 1; 219 | return 0; 220 | } 221 | } 222 | 223 | // no free space to track the nade 224 | return -1; 225 | } 226 | 227 | static inline void draw_item(float progress, vec4_t const color) 228 | { 229 | CG_FillRect( 230 | timer_.graph_xywh[0] + (timer_.graph_xywh[2] - timer_item_w.value) * progress, 231 | timer_.graph_xywh[1], 232 | timer_item_w.value, 233 | timer_.graph_xywh[3], 234 | color); 235 | } 236 | -------------------------------------------------------------------------------- /src/cg_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | ============================== 3 | Written by id software, nightmare and hk of mdd 4 | This file is part of mdd client proxymod. 5 | 6 | mdd client proxymod is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | mdd client proxymod is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with mdd client proxymod. If not, see . 18 | ============================== 19 | Note: mdd client proxymod contains large quantities from the quake III arena source code 20 | */ 21 | #include "cg_utils.h" 22 | 23 | #include "cg_cvar.h" 24 | #include "cg_local.h" 25 | #include "cg_vm.h" 26 | #include "defrag.h" 27 | 28 | snapshot_t const* getSnap(void) 29 | { 30 | static snapshot_t snapshot; 31 | int32_t curSnapNum; 32 | int32_t servertime; 33 | trap_GetCurrentSnapshotNumber(&curSnapNum, &servertime); 34 | trap_GetSnapshot(curSnapNum, &snapshot); 35 | return &snapshot; 36 | } 37 | 38 | playerState_t const* getPs(void) 39 | { 40 | if (cvar_getInteger("g_synchronousClients")) return &getSnap()->ps; 41 | return (playerState_t const*)VM_ArgPtr(defrag()->pps_offset); 42 | } 43 | -------------------------------------------------------------------------------- /src/cg_view.c: -------------------------------------------------------------------------------- 1 | #include "cg_cvar.h" 2 | #include "cg_local.h" 3 | #include "q_assert.h" 4 | 5 | /* 6 | ================= 7 | CG_CalcVrect 8 | 9 | Sets the coordinates of the rendered window 10 | ================= 11 | */ 12 | static void CG_CalcVrect(void) 13 | { 14 | cg.refdef.width = cgs.glconfig.vidWidth; 15 | 16 | cg.refdef.height = cgs.glconfig.vidHeight; 17 | 18 | cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width) / 2; 19 | cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height) / 2; 20 | } 21 | 22 | /* 23 | ==================== 24 | CG_CalcFov 25 | 26 | Fixed fov at intermissions, otherwise account for fov variable and zooms. 27 | ==================== 28 | */ 29 | static void CG_CalcFov(void) 30 | { 31 | float fov_x = mdd_fov.value > 0 ? mdd_fov.value : cvar_getValue("cg_fov"); 32 | ASSERT_GT(fov_x, 0); 33 | 34 | // set it 35 | cg.refdef.fov_x = DEG2RAD(fov_x); 36 | cg.refdef.fov_y = atan2f((float)cg.refdef.height, cg.refdef.width / tanf(cg.refdef.fov_x / 2)) * 2; 37 | } 38 | 39 | /* 40 | =============== 41 | CG_CalcViewValues 42 | 43 | Sets cg.refdef view values 44 | =============== 45 | */ 46 | static void CG_CalcViewValues(void) 47 | { 48 | memset(&cg.refdef, 0, sizeof(cg.refdef)); 49 | 50 | // calculate size of 3D view 51 | CG_CalcVrect(); 52 | 53 | // field of view 54 | CG_CalcFov(); 55 | } 56 | 57 | /* 58 | ================= 59 | CG_DrawActiveFrame 60 | 61 | Generates and draws a game scene and status information at the given time. 62 | ================= 63 | */ 64 | void CG_DrawActiveFrame(int32_t serverTime, stereoFrame_t stereoView, qboolean demoPlayback) 65 | { 66 | (void)stereoView; 67 | 68 | cg.time = serverTime; 69 | cg.demoPlayback = demoPlayback; 70 | 71 | // update cvars 72 | CG_UpdateCvars(); 73 | 74 | // build cg.refdef 75 | CG_CalcViewValues(); 76 | 77 | // finish up the rest of the refdef 78 | cg.refdef.time = cg.time; 79 | } 80 | -------------------------------------------------------------------------------- /src/compass.c: -------------------------------------------------------------------------------- 1 | #include "compass.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_draw.h" 5 | #include "cg_utils.h" 6 | #include "help.h" 7 | 8 | static vmCvar_t compass; 9 | static vmCvar_t compass_yh; 10 | static vmCvar_t compass_quadrant_rgbas; 11 | static vmCvar_t compass_ticks_rgba; 12 | static vmCvar_t compass_arrow_rgbas; 13 | 14 | static cvarTable_t compass_cvars[] = { 15 | { &compass, "mdd_compass", "0b000", CVAR_ARCHIVE_ND }, 16 | { &compass_yh, "mdd_compass_yh", "188 8", CVAR_ARCHIVE_ND }, 17 | { &compass_quadrant_rgbas, 18 | "mdd_compass_quadrant_rgbas", 19 | "1 1 0 .25 / 0 1 0 .25 / 0 0 1 .25 / 1 0 1 .25", 20 | CVAR_ARCHIVE_ND }, 21 | { &compass_ticks_rgba, "mdd_compass_ticks_rgba", "1 1 1 1", CVAR_ARCHIVE_ND }, 22 | { &compass_arrow_rgbas, "mdd_compass_arrow_rgbas", "1 1 1 1 / 1 .5 0 1", CVAR_ARCHIVE_ND }, 23 | }; 24 | 25 | static help_t compass_help[] = { 26 | { compass_cvars + 0, 27 | BINARY_LITERAL, 28 | { 29 | "mdd_compass 0bXXX", 30 | " |||", 31 | " ||+- draw quadrants", 32 | " |+-- draw ticks", 33 | " +--- draw arrow", 34 | } }, 35 | #define QUADRANTS 1 36 | #define TICKS 2 37 | #define ARROW 4 38 | { 39 | compass_cvars + 1, 40 | Y | H, 41 | { 42 | "mdd_compass_yh X X", 43 | }, 44 | }, 45 | { 46 | compass_cvars + 2, 47 | RGBAS, 48 | { 49 | "mdd_compass_quadrant_rgbas X X X X / X X X X / X X X X / X X X X", 50 | }, 51 | }, 52 | { 53 | compass_cvars + 3, 54 | RGBA, 55 | { 56 | "mdd_compass_ticks_rgba X X X X", 57 | }, 58 | }, 59 | { 60 | compass_cvars + 4, 61 | RGBAS, 62 | { 63 | "mdd_compass_arrow_rgbas X X X X / X X X X", 64 | }, 65 | }, 66 | }; 67 | 68 | void init_compass(void) 69 | { 70 | init_cvars(compass_cvars, ARRAY_LEN(compass_cvars)); 71 | init_help(compass_help, ARRAY_LEN(compass_help)); 72 | } 73 | 74 | void update_compass(void) 75 | { 76 | update_cvars(compass_cvars, ARRAY_LEN(compass_cvars)); 77 | compass.integer = cvar_getInteger("mdd_compass"); 78 | } 79 | 80 | typedef struct 81 | { 82 | vec2_t graph_yh; 83 | vec4_t graph_quadrant_rgbas[4]; 84 | vec4_t graph_arrow_rgba[2]; 85 | vec4_t graph_ticks_rgba; 86 | 87 | playerState_t pm_ps; 88 | } compass_t; 89 | 90 | static compass_t s; 91 | 92 | void draw_compass(void) 93 | { 94 | if (!compass.integer) return; 95 | 96 | ParseVec(compass_yh.string, s.graph_yh, 2); 97 | 98 | s.pm_ps = *getPs(); 99 | 100 | float const yaw = DEG2RAD(s.pm_ps.viewangles[YAW]); 101 | 102 | if (compass.integer & QUADRANTS) 103 | { 104 | ParseVec4(compass_quadrant_rgbas.string, s.graph_quadrant_rgbas, 4); 105 | CG_FillAngleYaw(0, (float)M_PI / 2, yaw, s.graph_yh[0], s.graph_yh[1], s.graph_quadrant_rgbas[0]); 106 | CG_FillAngleYaw((float)M_PI / 2, (float)M_PI, yaw, s.graph_yh[0], s.graph_yh[1], s.graph_quadrant_rgbas[1]); 107 | CG_FillAngleYaw(-(float)M_PI / 2, -(float)M_PI, yaw, s.graph_yh[0], s.graph_yh[1], s.graph_quadrant_rgbas[2]); 108 | CG_FillAngleYaw(0, -(float)M_PI / 2, yaw, s.graph_yh[0], s.graph_yh[1], s.graph_quadrant_rgbas[3]); 109 | } 110 | 111 | if (compass.integer & TICKS) 112 | { 113 | ParseVec(compass_ticks_rgba.string, s.graph_ticks_rgba, 4); 114 | { 115 | float const y = s.graph_yh[0] + s.graph_yh[1] / 2; 116 | float const w = 1; 117 | float const h = s.graph_yh[1] / 2; 118 | CG_DrawLineYaw(0, yaw, y, w, h, s.graph_ticks_rgba); 119 | CG_DrawLineYaw((float)M_PI / 2, yaw, y, w, h, s.graph_ticks_rgba); 120 | CG_DrawLineYaw((float)M_PI, yaw, y, w, h, s.graph_ticks_rgba); 121 | CG_DrawLineYaw(-(float)M_PI / 2, yaw, y, w, h, s.graph_ticks_rgba); 122 | } 123 | { 124 | float const y = s.graph_yh[0] + 3 * s.graph_yh[1] / 4; 125 | float const w = 1; 126 | float const h = s.graph_yh[1] / 4; 127 | CG_DrawLineYaw((float)M_PI / 4, yaw, y, w, h, s.graph_ticks_rgba); 128 | CG_DrawLineYaw(3 * (float)M_PI / 4, yaw, y, w, h, s.graph_ticks_rgba); 129 | CG_DrawLineYaw(-(float)M_PI / 4, yaw, y, w, h, s.graph_ticks_rgba); 130 | CG_DrawLineYaw(-3 * (float)M_PI / 4, yaw, y, w, h, s.graph_ticks_rgba); 131 | } 132 | } 133 | 134 | if (compass.integer & ARROW && (s.pm_ps.velocity[0] != 0 || s.pm_ps.velocity[1] != 0)) 135 | { 136 | ParseVec4(compass_arrow_rgbas.string, s.graph_arrow_rgba, 2); 137 | vec4_t* color = &s.graph_arrow_rgba[0]; 138 | if (s.pm_ps.velocity[0] == 0 || s.pm_ps.velocity[1] == 0) 139 | { 140 | color = &s.graph_arrow_rgba[1]; 141 | } 142 | 143 | float const v_dir = atan2f(s.pm_ps.velocity[1], s.pm_ps.velocity[0]); 144 | 145 | float const y = s.graph_yh[0] + s.graph_yh[1]; 146 | float const w = s.graph_yh[1]; 147 | float const h = s.graph_yh[1] / 2; 148 | 149 | uint8_t const ch_up = 135; // Arrow pointing up. 150 | uint8_t const ch_down = 134; // Arrow pointing down. 151 | 152 | CG_DrawCharYaw(v_dir, yaw, y, w, h, ch_up, *color); 153 | CG_DrawCharYaw(v_dir, yaw - (float)M_PI, y, w, h, ch_down, *color); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/compass.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPASS_H 2 | #define COMPASS_H 3 | 4 | void init_compass(void); 5 | 6 | void update_compass(void); 7 | 8 | void draw_compass(void); 9 | 10 | #endif // COMPASS_H 11 | -------------------------------------------------------------------------------- /src/defrag.c: -------------------------------------------------------------------------------- 1 | #include "defrag.h" 2 | 3 | #include "cg_local.h" 4 | #include "q_assert.h" 5 | 6 | #include 7 | 8 | static defrag_t const* defrag_version; 9 | 10 | defrag_t defrag_versions[] = { 11 | { 12 | "1.91.24", // name 13 | 0xF9C2764A, // crc32sum 14 | 0x000E9D70, // pps_offset 15 | 0x00002948, // cg_draw2d_defrag (0x00002924) 16 | 0x0001CBAF, // cg_draw2d_vanilla (0x0001CB74) 17 | }, 18 | { 19 | "1.91.25", // name 20 | 0x04150518, // crc32sum 21 | 0x000E9D98, // pps_offset 22 | 0x00002948, // cg_draw2d_defrag (0x00002924) 23 | 0x0001CBD0, // cg_draw2d_vanilla (0x0001CB95) 24 | }, 25 | { 26 | "1.91.26", // name 27 | 0xC2840107, // crc32sum 28 | 0x000E9DA0, // pps_offset 29 | 0x0000297A, // cg_draw2d_defrag (0x00002956) 30 | 0x0001CC04, // cg_draw2d_vanilla (0x0001CBC9) 31 | }, 32 | { 33 | "1.91.27", // name 34 | 0x8D53765B, // crc32sum 35 | 0x000EA09C, // pps_offset 36 | 0x00002A09, // cg_draw2d_defrag (0x000029E5) 37 | 0x0001CD2C, // cg_draw2d_vanilla (0x0001CCF1) 38 | }, 39 | // 1.91.28 requires VM_MAGIC_VER2 support 40 | { 41 | "1.91.29", // name 42 | 0xE5F9882A, // crc32sum 43 | 0x000EA11C, // pps_offset 44 | 0x00002A20, // cg_draw2d_defrag (0x000029FC) 45 | 0x0001CD87, // cg_draw2d_vanilla (0x0001CD4C) 46 | }, 47 | { 48 | "1.91.30", // name 49 | 0x03BDC08B, // crc32sum 50 | 0x000EA11C, // pps_offset 51 | 0x00002A2C, // cg_draw2d_defrag (0x00002A08) 52 | 0x0001CD93, // cg_draw2d_vanilla (0x0001CD58) 53 | }, 54 | { 55 | "1.91.31", // name 56 | 0x50AD1672, // crc32sum 57 | 0x000EA13C, // pps_offset 58 | 0x00002A36, // cg_draw2d_defrag (0x00002A12) 59 | 0x0001CDC1, // cg_draw2d_vanilla (0x0001CD86) 60 | }, 61 | }; 62 | 63 | qboolean init_defrag(uint32_t crc32sum) 64 | { 65 | for (size_t i = 0, n = ARRAY_LEN(defrag_versions); i < n; ++i) 66 | { 67 | defrag_version = &defrag_versions[i]; 68 | if (defrag_version->crc32sum == crc32sum) return qtrue; 69 | } 70 | defrag_version = NULL; 71 | 72 | // Report error about unsupported defrag version 73 | static_assert(ARRAY_LEN(defrag_versions) > 0, ""); 74 | size_t len = strlen(defrag_versions[0].name); 75 | for (size_t i = 1, n = ARRAY_LEN(defrag_versions); i < n; ++i) 76 | { 77 | len += 2; // ", " 78 | len += strlen(defrag_versions[i].name); 79 | } 80 | char* const versions = malloc(len + 1); 81 | strcpy(versions, defrag_versions[0].name); 82 | for (size_t i = 1, n = ARRAY_LEN(defrag_versions); i < n; ++i) 83 | { 84 | strcat(versions, ", "); 85 | strcat(versions, defrag_versions[i].name); 86 | } 87 | trap_Error(vaf( 88 | "The proxymod does not support your current defrag version, it only supports %s. " 89 | "Download the latest version from https://q3defrag.org/files/defrag.\n", 90 | versions)); 91 | return qfalse; 92 | } 93 | 94 | defrag_t const* defrag(void) 95 | { 96 | assert(defrag_version); 97 | return defrag_version; 98 | } 99 | -------------------------------------------------------------------------------- /src/defrag.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFRAG_H 2 | #define DEFRAG_H 3 | 4 | #include "q_shared.h" 5 | 6 | typedef struct 7 | { 8 | char const* name; 9 | 10 | uint32_t crc32sum; 11 | 12 | int32_t pps_offset; 13 | 14 | int32_t cg_draw2d_defrag; 15 | int32_t cg_draw2d_vanilla; 16 | } defrag_t; 17 | 18 | qboolean init_defrag(uint32_t crc32sum); 19 | 20 | defrag_t const* defrag(void); 21 | 22 | #endif // DEFRAG_H 23 | -------------------------------------------------------------------------------- /src/g_missile.c: -------------------------------------------------------------------------------- 1 | #include "cg_local.h" 2 | #include "g_local.h" 3 | 4 | #define MISSILE_PRESTEP_TIME 50 5 | 6 | /* 7 | ================= 8 | fire_grenade 9 | ================= 10 | */ 11 | void fire_grenade(gentity_t* bolt, gentity_t const* self, vec3_t const start, vec3_t dir) 12 | { 13 | VectorNormalize(dir); 14 | 15 | bolt->nextthink = cg.time + 2500; 16 | bolt->s.eType = ET_MISSILE; 17 | bolt->s.weapon = WP_GRENADE_LAUNCHER; 18 | bolt->s.eFlags = EF_BOUNCE_HALF; 19 | bolt->r.ownerNum = self->s.number; 20 | bolt->parent = self; 21 | bolt->damage = 100; 22 | bolt->splashDamage = 100; 23 | bolt->splashRadius = 150; 24 | bolt->clipmask = MASK_SHOT; 25 | bolt->target_ent = NULL; 26 | 27 | bolt->s.pos.trType = TR_GRAVITY; 28 | bolt->s.pos.trTime = cg.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 29 | VectorCopy(start, bolt->s.pos.trBase); 30 | VectorScale(dir, 700, bolt->s.pos.trDelta); 31 | SnapVector(bolt->s.pos.trDelta); // save net bandwidth 32 | 33 | VectorCopy(start, bolt->r.currentOrigin); 34 | } 35 | 36 | /* 37 | ================= 38 | fire_rocket 39 | ================= 40 | */ 41 | void fire_rocket(gentity_t* bolt, gentity_t const* self, vec3_t const start, vec3_t dir) 42 | { 43 | VectorNormalize(dir); 44 | 45 | bolt->nextthink = cg.time + 15000; 46 | bolt->s.eType = ET_MISSILE; 47 | bolt->s.weapon = WP_ROCKET_LAUNCHER; 48 | bolt->r.ownerNum = self->s.number; 49 | bolt->parent = self; 50 | bolt->damage = 100; 51 | bolt->splashDamage = 100; 52 | bolt->splashRadius = 120; 53 | bolt->clipmask = MASK_SHOT; 54 | bolt->target_ent = NULL; 55 | 56 | bolt->s.pos.trType = TR_LINEAR; 57 | bolt->s.pos.trTime = cg.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 58 | VectorCopy(start, bolt->s.pos.trBase); 59 | VectorScale(dir, 900, bolt->s.pos.trDelta); 60 | SnapVector(bolt->s.pos.trDelta); // save net bandwidth 61 | 62 | VectorCopy(start, bolt->r.currentOrigin); 63 | } 64 | -------------------------------------------------------------------------------- /src/g_weapon.c: -------------------------------------------------------------------------------- 1 | #include "bg_public.h" 2 | #include "cg_cvar.h" 3 | #include "g_local.h" 4 | 5 | static float s_quadFactor; 6 | static vec3_t forward; 7 | static vec3_t muzzle; 8 | 9 | /* 10 | ====================================================================== 11 | 12 | GRENADE LAUNCHER 13 | 14 | ====================================================================== 15 | */ 16 | static void weapon_grenadelauncher_fire(gentity_t* m, gentity_t const* ent) 17 | { 18 | // extra vertical velocity 19 | forward[2] += .2f; 20 | VectorNormalize(forward); 21 | 22 | fire_grenade(m, ent, muzzle, forward); 23 | m->damage = (int32_t)(m->damage * s_quadFactor); 24 | m->splashDamage = (int32_t)(m->splashDamage * s_quadFactor); 25 | 26 | // VectorAdd(m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta); // "real" physics 27 | } 28 | 29 | /* 30 | ====================================================================== 31 | 32 | ROCKET 33 | 34 | ====================================================================== 35 | */ 36 | static void Weapon_RocketLauncher_Fire(gentity_t* m, gentity_t const* ent) 37 | { 38 | fire_rocket(m, ent, muzzle, forward); 39 | m->damage = (int32_t)(m->damage * s_quadFactor); 40 | m->splashDamage = (int32_t)(m->splashDamage * s_quadFactor); 41 | 42 | // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics 43 | } 44 | 45 | /* 46 | =============== 47 | CalcMuzzlePointOrigin 48 | 49 | set muzzle location relative to pivoting eye 50 | =============== 51 | */ 52 | static void CalcMuzzlePointOrigin(playerState_t const* pm_ps, gentity_t const* ent, vec3_t muzzlePoint) 53 | { 54 | VectorCopy(ent->s.pos.trBase, muzzlePoint); 55 | muzzlePoint[2] += pm_ps->viewheight; 56 | VectorMA(muzzlePoint, 14, forward, muzzlePoint); 57 | // snap to integer coordinates for more efficient network bandwidth usage 58 | SnapVector(muzzlePoint); 59 | } 60 | 61 | /* 62 | =============== 63 | FireWeapon 64 | =============== 65 | */ 66 | void FireWeapon(playerState_t const* pm_ps, gentity_t* m, gentity_t const* ent) 67 | { 68 | if (pm_ps->powerups[PW_QUAD]) 69 | { 70 | s_quadFactor = cvar_getValue("g_quadfactor"); 71 | } 72 | else 73 | { 74 | s_quadFactor = 1; 75 | } 76 | 77 | // set aiming directions 78 | AngleVectors(pm_ps->viewangles, forward, NULL, NULL); 79 | 80 | CalcMuzzlePointOrigin(pm_ps, ent, muzzle); 81 | 82 | // fire the specific weapon 83 | switch (pm_ps->weapon) 84 | { 85 | // case WP_GAUNTLET: Weapon_Gauntlet(ent); break; 86 | // case WP_LIGHTNING: Weapon_LightningFire(ent); break; 87 | // case WP_SHOTGUN: weapon_supershotgun_fire(ent); break; 88 | // case WP_MACHINEGUN: 89 | // if (g_gametype.integer != GT_TEAM) 90 | // { 91 | // Bullet_Fire(ent, MACHINEGUN_SPREAD, g_q3p_mgdamage.value, MOD_MACHINEGUN); 92 | // } 93 | // else 94 | // { 95 | // Bullet_Fire(ent, MACHINEGUN_SPREAD, g_q3p_mgteamdamage.value, MOD_MACHINEGUN); 96 | // } 97 | // break; 98 | case WP_GRENADE_LAUNCHER: 99 | weapon_grenadelauncher_fire(m, ent); 100 | break; 101 | case WP_ROCKET_LAUNCHER: 102 | Weapon_RocketLauncher_Fire(m, ent); 103 | break; 104 | // case WP_PLASMAGUN: Weapon_Plasmagun_Fire(ent); break; 105 | // case WP_RAILGUN: weapon_railgun_fire(ent); break; 106 | // case WP_BFG: BFG_Fire(ent); break; 107 | // case WP_GRAPPLING_HOOK: Weapon_GrapplingHook_Fire(ent); break; 108 | default: 109 | // FIXME G_Error( "Bad ent->s.weapon" ); 110 | break; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/help.c: -------------------------------------------------------------------------------- 1 | #include "help.h" 2 | 3 | #include "q_assert.h" 4 | 5 | typedef struct 6 | { 7 | help_t const* help; 8 | size_t size; 9 | } helpTable_t; 10 | 11 | static size_t helpTableIdx = 0; 12 | static helpTable_t helpTable[10]; 13 | 14 | static void preHelp(cvarKind_t kind, char const* defaultString); 15 | static void postHelp(cvarKind_t kind); 16 | 17 | void init_help(help_t const* help, size_t size) 18 | { 19 | ASSERT_LT(helpTableIdx, ARRAY_LEN(helpTable)); 20 | #ifndef NDEBUG 21 | for (size_t i = 0; i < size; ++i) 22 | { 23 | size_t const len = strlen(help[i].cvarTable->cvarName); 24 | assert(strlen(help[i].message[0]) > len); 25 | assert(!Q_strncmp(help[i].message[0], help[i].cvarTable->cvarName, (int)len)); 26 | assert(help[i].message[0][len] == ' '); 27 | } 28 | #endif 29 | helpTable[helpTableIdx].help = help; 30 | helpTable[helpTableIdx].size = size; 31 | ++helpTableIdx; 32 | } 33 | 34 | void del_help(void) 35 | { 36 | helpTableIdx = 0; 37 | } 38 | 39 | void cvar_help(char const* cvarName) 40 | { 41 | for (size_t i = 0; i < helpTableIdx; ++i) 42 | { 43 | for (size_t j = 0; j < helpTable[i].size; ++j) 44 | { 45 | help_t const* help = &helpTable[i].help[j]; 46 | if (Q_stricmp(cvarName, help->cvarTable->cvarName)) continue; 47 | 48 | assert(help->message); 49 | preHelp(help->kind, help->cvarTable->defaultString); 50 | for (size_t k = 0; k < ARRAY_LEN(help->message); ++k) 51 | { 52 | char const* message = help->message[k]; 53 | if (!message) break; 54 | trap_Print(vaf("^3%s^7\n", message)); 55 | } 56 | postHelp(help->kind); 57 | return; 58 | } 59 | } 60 | 61 | cvars_with_help(); 62 | } 63 | 64 | void cvars_with_help(void) 65 | { 66 | trap_Print("^3There is help for the cvars:^7\n"); 67 | for (size_t i = 0; i < ARRAY_LEN(helpTable); ++i) 68 | { 69 | for (size_t j = 0; j < helpTable[i].size; ++j) 70 | { 71 | help_t const* help = &helpTable[i].help[j]; 72 | trap_Print(vaf(" ^2%s^7\n", help->cvarTable->cvarName)); 73 | } 74 | } 75 | } 76 | 77 | static void preHelp(cvarKind_t kind, char const* defaultString) 78 | { 79 | ASSERT_GT(kind, 0); 80 | if (kind < 16) 81 | { 82 | cvarKind_t n = kind; 83 | uint8_t count = 0; 84 | while (n) 85 | { 86 | count += n & 1; 87 | n >>= 1; 88 | } 89 | ASSERT_GE(count, 1); 90 | ASSERT_LE(count, 4); 91 | trap_Print(vaf("^2This cvar accepts %u %s:^7\n", count, count == 1 ? "number" : "numbers")); 92 | 93 | float const x = cgs.glconfig.vidWidth / cgs.screenXScale; 94 | float const y = cgs.glconfig.vidHeight / cgs.screenXScale; 95 | if (kind & X) trap_Print(vaf(" ^3x^2-coordinate [0,%.0f]^7\n", x)); 96 | if (kind & Y) trap_Print(vaf(" ^3y^2-coordinate [0,%.0f]^7\n", y)); 97 | if (kind & W) trap_Print(vaf(" ^3w^2idth [0,%.0f]^7\n", x)); 98 | if (kind & H) trap_Print(vaf(" ^3h^2eight [0,%.0f]^7\n", y)); 99 | return; 100 | } 101 | 102 | ASSERT_GE(kind, 16); 103 | switch (kind) 104 | { 105 | case BINARY_LITERAL: 106 | trap_Print("^2This cvar can accept binary-literals - a sequence starting^7\n"); 107 | trap_Print(vaf("^2with ^30b^2 followed by ^31^2's and ^30^2's (e.g. ^3%s^2).^7\n", defaultString)); 108 | trap_Print("^2Enable/disable items by replacing ^3X^2's with ^31^2/^30^2 resp.\n"); 109 | return; 110 | case RGBA: 111 | trap_Print("^2This cvar accepts a set of 4 numbers:^7\n"); 112 | trap_Print(" ^3r^2ed [0,1]^7\n"); 113 | trap_Print(" ^3g^2reen [0,1]^7\n"); 114 | trap_Print(" ^3b^2lue [0,1]^7\n"); 115 | trap_Print(" ^3a^2lpha [0,1]^7\n"); 116 | return; 117 | case RGBAS: 118 | trap_Print("^2This cvar accepts sets of 4 numbers:^7\n"); 119 | trap_Print(" ^3r^2ed [0,1]^7\n"); 120 | trap_Print(" ^3g^2reen [0,1]^7\n"); 121 | trap_Print(" ^3b^2lue [0,1]^7\n"); 122 | trap_Print(" ^3a^2lpha [0,1]^7\n"); 123 | return; 124 | default: 125 | assert(0); 126 | return; 127 | } 128 | } 129 | 130 | static void postHelp(cvarKind_t kind) 131 | { 132 | ASSERT_GT(kind, 0); 133 | if (kind < 16) 134 | { 135 | return; 136 | } 137 | 138 | ASSERT_GE(kind, 16); 139 | switch (kind) 140 | { 141 | case BINARY_LITERAL: 142 | trap_Print("^2Examples: github.com/Jelvan1/cgame_proxymod#examples^7\n"); 143 | return; 144 | case RGBA: 145 | case RGBAS: 146 | return; 147 | default: 148 | assert(0); 149 | return; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/help.h: -------------------------------------------------------------------------------- 1 | #ifndef HELP_H 2 | #define HELP_H 3 | 4 | #include "cg_cvar.h" 5 | 6 | typedef enum 7 | { 8 | // X, Y, W and H can be combined, hence the spacing. 9 | X = 1, 10 | Y = 2, 11 | W = 4, 12 | H = 8, 13 | BINARY_LITERAL = 16, 14 | RGBA, 15 | RGBAS 16 | } cvarKind_t; 17 | 18 | typedef struct 19 | { 20 | cvarTable_t const* cvarTable; 21 | cvarKind_t kind; 22 | char const* message[7]; 23 | } help_t; 24 | 25 | void init_help(help_t const* help, size_t size); 26 | 27 | void del_help(void); 28 | 29 | void cvar_help(char const* cvarName); 30 | 31 | void cvars_with_help(void); 32 | 33 | #endif // HELP_H 34 | -------------------------------------------------------------------------------- /src/pitch.c: -------------------------------------------------------------------------------- 1 | #include "pitch.h" 2 | 3 | #include "cg_cvar.h" 4 | #include "cg_draw.h" 5 | #include "cg_utils.h" 6 | #include "help.h" 7 | 8 | #include 9 | 10 | static vmCvar_t pitch; 11 | static vmCvar_t pitch_xwh; 12 | static vmCvar_t pitch_rgba; 13 | 14 | static cvarTable_t pitch_cvars[] = { 15 | { &pitch, "mdd_pitch", "", CVAR_ARCHIVE_ND }, 16 | { &pitch_xwh, "mdd_pitch_xwh", "316 8 1", CVAR_ARCHIVE_ND }, 17 | { &pitch_rgba, "mdd_pitch_rgba", ".8 .8 .8 .8", CVAR_ARCHIVE_ND }, 18 | }; 19 | 20 | static help_t pitch_help[] = { 21 | { 22 | pitch_cvars + 1, 23 | X | W | H, 24 | { 25 | "mdd_pitch_xwh X X X", 26 | }, 27 | }, 28 | { 29 | pitch_cvars + 2, 30 | RGBA, 31 | { 32 | "mdd_pitch_rgba X X X X", 33 | }, 34 | }, 35 | }; 36 | 37 | void init_pitch(void) 38 | { 39 | init_cvars(pitch_cvars, ARRAY_LEN(pitch_cvars)); 40 | init_help(pitch_help, ARRAY_LEN(pitch_help)); 41 | } 42 | 43 | void update_pitch(void) 44 | { 45 | update_cvars(pitch_cvars, ARRAY_LEN(pitch_cvars)); 46 | } 47 | 48 | typedef struct 49 | { 50 | vec3_t graph_xwh; 51 | vec4_t graph_rgba; 52 | 53 | playerState_t pm_ps; 54 | } pitch_t; 55 | 56 | static pitch_t s; 57 | 58 | void draw_pitch(void) 59 | { 60 | if (pitch.string[0] == '\0') return; 61 | 62 | ParseVec(pitch_xwh.string, s.graph_xwh, 3); 63 | ParseVec(pitch_rgba.string, s.graph_rgba, 4); 64 | 65 | s.pm_ps = *getPs(); 66 | 67 | float const p = DEG2RAD(s.pm_ps.viewangles[PITCH]); 68 | 69 | char const* str = pitch.string; 70 | char* end; 71 | for (float angle = strtof(str, &end); str != end; angle = strtof(str, &end)) 72 | { 73 | float const x = s.graph_xwh[0]; 74 | float const w = s.graph_xwh[1]; 75 | float const h = s.graph_xwh[2]; 76 | CG_DrawLinePitch(DEG2RAD(angle), p, x, w, h, s.graph_rgba); 77 | str = end; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/pitch.h: -------------------------------------------------------------------------------- 1 | #ifndef PITCH_H 2 | #define PITCH_H 3 | 4 | void init_pitch(void); 5 | 6 | void update_pitch(void); 7 | 8 | void draw_pitch(void); 9 | 10 | #endif // PITCH_H 11 | -------------------------------------------------------------------------------- /src/q_shared.c: -------------------------------------------------------------------------------- 1 | #include "q_shared.h" 2 | 3 | #include 4 | 5 | int32_t LongSwap(int32_t l) 6 | { 7 | byte b1, b2, b3, b4; 8 | 9 | b1 = l & 255; 10 | b2 = (l >> 8) & 255; 11 | b3 = (l >> 16) & 255; 12 | b4 = (l >> 24) & 255; 13 | 14 | return ((int32_t)b1 << 24) + ((int32_t)b2 << 16) + ((int32_t)b3 << 8) + b4; 15 | } 16 | 17 | int Q_stricmpn(const char* s1, const char* s2, int n) 18 | { 19 | int c1, c2; 20 | 21 | if (s1 == NULL) 22 | { 23 | if (s2 == NULL) 24 | return 0; 25 | else 26 | return -1; 27 | } 28 | else if (s2 == NULL) 29 | return 1; 30 | 31 | do 32 | { 33 | c1 = *s1++; 34 | c2 = *s2++; 35 | 36 | if (!n--) 37 | { 38 | return 0; // strings are equal until end point 39 | } 40 | 41 | if (c1 != c2) 42 | { 43 | if (c1 >= 'a' && c1 <= 'z') 44 | { 45 | c1 -= ('a' - 'A'); 46 | } 47 | if (c2 >= 'a' && c2 <= 'z') 48 | { 49 | c2 -= ('a' - 'A'); 50 | } 51 | if (c1 != c2) 52 | { 53 | return c1 < c2 ? -1 : 1; 54 | } 55 | } 56 | } while (c1); 57 | 58 | return 0; // strings are equal 59 | } 60 | 61 | int Q_strncmp(const char* s1, const char* s2, int n) 62 | { 63 | int c1, c2; 64 | 65 | do 66 | { 67 | c1 = *s1++; 68 | c2 = *s2++; 69 | 70 | if (!n--) 71 | { 72 | return 0; // strings are equal until end point 73 | } 74 | 75 | if (c1 != c2) 76 | { 77 | return c1 < c2 ? -1 : 1; 78 | } 79 | } while (c1); 80 | 81 | return 0; // strings are equal 82 | } 83 | 84 | int Q_stricmp(const char* s1, const char* s2) 85 | { 86 | return (s1 && s2) ? Q_stricmpn(s1, s2, 99999) : -1; 87 | } 88 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | add_executable(UnitTest 4 | syscalls.cpp 5 | syscalls_client_fake.cpp 6 | syscalls_cvar_fake.cpp 7 | syscalls_mock.cpp 8 | ) 9 | 10 | target_compile_features(UnitTest PUBLIC cxx_std_17) 11 | 12 | if(MSVC) 13 | target_compile_options(UnitTest PRIVATE 14 | /WX 15 | /W4 16 | /wd4996 17 | ) 18 | target_link_options(UnitTest PRIVATE /WX) 19 | else() 20 | target_compile_options(UnitTest PRIVATE 21 | -Werror 22 | -Wall 23 | -Wextra 24 | -pedantic-errors 25 | -Wshadow 26 | 27 | $<$:-Wmissing-prototypes> 28 | $<$:-Wstrict-prototypes> 29 | $<$:-Wunreachable-code-return> 30 | $<$:-Wunreachable-code> 31 | 32 | $<$:-Wold-style-cast> 33 | $<$:-Wunreachable-code-return> 34 | $<$:-Wunreachable-code> 35 | ) 36 | endif() 37 | 38 | target_link_libraries(UnitTest 39 | PRIVATE cgame_obj 40 | PRIVATE gmock 41 | PRIVATE gtest_main 42 | ) 43 | 44 | include(GoogleTest) 45 | gtest_discover_tests(UnitTest) 46 | -------------------------------------------------------------------------------- /test/syscalls.cpp: -------------------------------------------------------------------------------- 1 | #include "syscalls.hpp" 2 | 3 | extern "C" 4 | { 5 | #include 6 | } 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Max number of arguments to pass from a vm to engine's syscall handler function for the vm. 13 | // syscall number + 15 arguments 14 | #define MAX_VMSYSCALL_ARGS 16 15 | 16 | namespace 17 | { 18 | template 19 | T* ptr(std::intptr_t x) 20 | { 21 | return reinterpret_cast(x); 22 | } 23 | } // namespace 24 | 25 | Syscalls::Syscalls() 26 | { 27 | assert(!syscalls_); 28 | syscalls_ = this; 29 | dllEntry(&VM_DllSyscall); 30 | } 31 | 32 | Syscalls::~Syscalls() 33 | { 34 | assert(syscalls_ == this); 35 | syscalls_ = nullptr; 36 | dllEntry(nullptr); 37 | } 38 | 39 | /* 40 | ============ 41 | VM_DllSyscall 42 | 43 | Dlls will call this directly 44 | 45 | rcg010206 The horror; the horror. 46 | 47 | The syscall mechanism relies on stack manipulation to get its args. 48 | This is likely due to C's inability to pass "..." parameters to 49 | a function in one clean chunk. On PowerPC Linux, these parameters 50 | are not necessarily passed on the stack, so while (&arg[0] == arg) 51 | is true, (&arg[1] == 2nd function parameter) is not necessarily 52 | accurate, as arg's value might have been stored to the stack or 53 | other piece of scratch memory to give it a valid address, but the 54 | next parameter might still be sitting in a register. 55 | 56 | Quake's syscall system also assumes that the stack grows downward, 57 | and that any needed types can be squeezed, safely, into a signed int. 58 | 59 | This hack below copies all needed values for an argument to a 60 | array in memory, so that Quake can get the correct values. This can 61 | also be used on systems where the stack grows upwards, as the 62 | presumably standard and safe stdargs.h macros are used. 63 | 64 | As for having enough space in a signed int for your datatypes, well, 65 | it might be better to wait for DOOM 3 before you start porting. :) 66 | 67 | The original code, while probably still inherently dangerous, seems 68 | to work well enough for the platforms it already works on. Rather 69 | than add the performance hit for those platforms, the original code 70 | is still in use there. 71 | 72 | For speed, we just grab 15 arguments, and don't worry about exactly 73 | how many the syscall actually needs; the extra is thrown away. 74 | 75 | ============ 76 | */ 77 | std::intptr_t QDECL Syscalls::VM_DllSyscall(std::intptr_t arg, ...) 78 | { 79 | assert(syscalls_); 80 | 81 | // rcg010206 - see commentary above 82 | std::intptr_t args[MAX_VMSYSCALL_ARGS]; 83 | args[0] = arg; 84 | 85 | std::va_list ap; 86 | va_start(ap, arg); 87 | for (std::uint8_t i = 1; i < ARRAY_LEN(args); i++) args[i] = va_arg(ap, std::intptr_t); 88 | va_end(ap); 89 | 90 | return syscalls_->CL_CgameSystemCalls(args[0], args + 1); 91 | } 92 | 93 | /* 94 | ==================== 95 | CL_CgameSystemCalls 96 | 97 | The cgame module is making a system call 98 | ==================== 99 | */ 100 | std::intptr_t Syscalls::CL_CgameSystemCalls(std::intptr_t cmd, std::intptr_t* args) 101 | { 102 | switch (cmd) 103 | { 104 | case CG_CVAR_REGISTER: 105 | Cvar_Register( 106 | ptr(args[0]), ptr(args[1]), ptr(args[2]), static_cast(args[3])); 107 | return 0; 108 | case CG_CVAR_UPDATE: 109 | Cvar_Update(ptr(args[0])); 110 | return 0; 111 | case CG_CVAR_SET: 112 | Cvar_SetSafe(ptr(args[0]), ptr(args[1])); 113 | return 0; 114 | case CG_CVAR_VARIABLESTRINGBUFFER: 115 | Cvar_VariableStringBufferSafe( 116 | ptr(args[0]), ptr(args[1]), static_cast(args[2]), CVAR_PRIVATE); 117 | return 0; 118 | case CG_GETCURRENTSNAPSHOTNUMBER: 119 | CL_GetCurrentSnapshotNumber(ptr(args[0]), ptr(args[1])); 120 | return 0; 121 | case CG_GETSNAPSHOT: 122 | return CL_GetSnapshot(static_cast(args[0]), ptr(args[1])); 123 | } 124 | assert(false); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /test/syscalls.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALLS_HPP 2 | #define SYSCALLS_HPP 3 | 4 | extern "C" 5 | { 6 | #include 7 | } 8 | 9 | #include 10 | 11 | class Syscalls 12 | { 13 | public: 14 | virtual void Cvar_Register(vmCvar_t* vmCvar, char const* varName, char const* defaultValue, std::int32_t flags) = 0; 15 | 16 | virtual void Cvar_Update(vmCvar_t* vmCvar) = 0; 17 | 18 | virtual void Cvar_SetSafe(char const* var_name, char const* value) = 0; 19 | 20 | virtual void Cvar_VariableStringBufferSafe( 21 | char const* var_name, 22 | char* buffer, 23 | std::int32_t bufsize, 24 | std::int32_t flag) = 0; 25 | 26 | virtual void CL_GetCurrentSnapshotNumber(std::int32_t* snapshotNumber, std::int32_t* serverTime) = 0; 27 | 28 | virtual qboolean CL_GetSnapshot(std::int32_t snapshotNumber, snapshot_t* snapshot) = 0; 29 | 30 | Syscalls(); 31 | 32 | virtual ~Syscalls(); 33 | 34 | private: 35 | static std::intptr_t QDECL VM_DllSyscall(std::intptr_t arg, ...); 36 | 37 | std::intptr_t CL_CgameSystemCalls(std::intptr_t cmd, std::intptr_t* args); 38 | 39 | private: 40 | inline static Syscalls* syscalls_ = nullptr; 41 | }; 42 | 43 | #endif // SYSCALLS_HPP 44 | -------------------------------------------------------------------------------- /test/syscalls_client_fake.cpp: -------------------------------------------------------------------------------- 1 | #include "syscalls_client_fake.hpp" 2 | 3 | #include "syscalls_mock.hpp" 4 | 5 | extern "C" 6 | { 7 | #include 8 | } 9 | 10 | #include 11 | #include 12 | 13 | #define PACKET_BACKUP \ 14 | 32 // number of old messages that must be kept on client and 15 | // server for delta comrpession and ping estimation 16 | #define PACKET_MASK (PACKET_BACKUP - 1) 17 | 18 | class SyscallsClientFake::Impl 19 | { 20 | public: 21 | Impl() : snapshots_(PACKET_BACKUP) 22 | { 23 | // Fake predicted player state, VM_ArgPtr(defrag()->pps_offset) 24 | std::memset(&g_VM, 0, sizeof(g_VM)); 25 | g_VM.dataSegment = reinterpret_cast(&pps_); 26 | } 27 | 28 | ~Impl() 29 | { 30 | assert(g_VM.dataSegment == reinterpret_cast(&pps_)); 31 | g_VM.dataSegment = nullptr; 32 | } 33 | 34 | void CL_GetCurrentSnapshotNumber(std::int32_t* snapshotNumber, std::int32_t* serverTime) const 35 | { 36 | assert(snapshotNumber); 37 | assert(serverTime); 38 | *snapshotNumber = snapshotNumber_; 39 | *serverTime = getSnapshot(snapshotNumber_).serverTime; 40 | } 41 | 42 | qboolean CL_GetSnapshot(std::int32_t snapshotNumber, snapshot_t* snapshot) const 43 | { 44 | assert(snapshot); 45 | std::memcpy(snapshot, &getSnapshot(snapshotNumber), sizeof(snapshot_t)); 46 | return qtrue; 47 | } 48 | 49 | const snapshot_t& getSnapshot(std::int32_t snapshotNumber) const 50 | { 51 | return snapshots_[snapshotNumber & PACKET_MASK]; 52 | } 53 | 54 | snapshot_t& getSnapshot(std::int32_t snapshotNumber) 55 | { 56 | return snapshots_[snapshotNumber & PACKET_MASK]; 57 | } 58 | 59 | public: 60 | std::int32_t snapshotNumber_ = 0; 61 | std::vector snapshots_; 62 | 63 | playerState_t pps_ = {}; 64 | }; 65 | 66 | snapshot_t& SyscallsClientFake::getSnapshot() 67 | { 68 | return impl_->getSnapshot(impl_->snapshotNumber_); 69 | } 70 | 71 | playerState_t& SyscallsClientFake::getPlayerState() 72 | { 73 | return impl_->pps_; 74 | } 75 | 76 | void SyscallsClientFake::setDefaultActions(SyscallsMock& mock) 77 | { 78 | ON_CALL(mock, CL_GetCurrentSnapshotNumber) 79 | .WillByDefault(testing::Invoke(impl_.get(), &Impl::CL_GetCurrentSnapshotNumber)); 80 | ON_CALL(mock, CL_GetSnapshot).WillByDefault(testing::Invoke(impl_.get(), &Impl::CL_GetSnapshot)); 81 | } 82 | 83 | SyscallsClientFake::SyscallsClientFake() : impl_(std::make_unique()) 84 | { 85 | } 86 | 87 | SyscallsClientFake::~SyscallsClientFake() = default; 88 | 89 | TEST(SyscallsClientFake, GetCurrentSnapshotNumber) 90 | { 91 | SyscallsClientFake::Impl fake; 92 | fake.snapshotNumber_ = 1; 93 | fake.getSnapshot(1).serverTime = 2; 94 | std::int32_t snapshotNumber = 0; 95 | std::int32_t serverTime = 0; 96 | 97 | fake.CL_GetCurrentSnapshotNumber(&snapshotNumber, &serverTime); 98 | EXPECT_EQ(snapshotNumber, 1); 99 | EXPECT_EQ(serverTime, 2); 100 | } 101 | 102 | TEST(SyscallsClientFake, GetSnapshot) 103 | { 104 | SyscallsClientFake::Impl fake; 105 | fake.getSnapshot(1).serverTime = 123; 106 | fake.getSnapshot(2).serverTime = 321; 107 | snapshot_t snap1 = {}; 108 | snapshot_t snap2 = {}; 109 | 110 | fake.CL_GetSnapshot(1, &snap1); 111 | fake.CL_GetSnapshot(2, &snap2); 112 | EXPECT_EQ(snap1.serverTime, 123); 113 | EXPECT_EQ(snap2.serverTime, 321); 114 | } 115 | -------------------------------------------------------------------------------- /test/syscalls_client_fake.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALLS_CLIENT_FAKE_HPP 2 | #define SYSCALLS_CLIENT_FAKE_HPP 3 | 4 | #include "syscalls_fake.hpp" 5 | 6 | extern "C" 7 | { 8 | #include 9 | } 10 | 11 | #include 12 | 13 | class SyscallsClientFake : public SyscallsFake 14 | { 15 | public: 16 | snapshot_t& getSnapshot(); 17 | 18 | playerState_t& getPlayerState(); 19 | 20 | void setDefaultActions(SyscallsMock& mock) final; 21 | 22 | SyscallsClientFake(); 23 | 24 | ~SyscallsClientFake(); 25 | 26 | class Impl; 27 | 28 | private: 29 | std::unique_ptr impl_; 30 | }; 31 | 32 | #endif // SYSCALLS_CLIENT_FAKE_HPP 33 | -------------------------------------------------------------------------------- /test/syscalls_cvar_fake.cpp: -------------------------------------------------------------------------------- 1 | #include "syscalls_cvar_fake.hpp" 2 | 3 | #include "syscalls_mock.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace 10 | { 11 | class Cvar 12 | { 13 | public: 14 | Cvar(char const* string, std::int32_t flags) : flags_(flags) 15 | { 16 | assert(string); 17 | set(string); 18 | } 19 | 20 | void set(char const* string) 21 | { 22 | assert(string); 23 | string_ = string; 24 | value_ = static_cast(std::atof(string)); 25 | integer_ = std::atoi(string); 26 | } 27 | 28 | std::string string_; 29 | std::int32_t flags_ = 0; 30 | float value_ = 0; // std::atof(string) 31 | std::int32_t integer_ = 0; // std::atoi(string) 32 | }; 33 | } // namespace 34 | 35 | class SyscallsCvarFake::Impl 36 | { 37 | public: 38 | void Cvar_Register(vmCvar_t* vmCvar, char const* varName, char const* defaultValue, std::int32_t flags) 39 | { 40 | assert(vmCvar); 41 | assert(varName); 42 | assert(defaultValue); 43 | 44 | assert(!nameToHandles_.count(varName)); 45 | nameToHandles_.emplace(varName, nextCvarHandle_); 46 | cvars_.emplace_back(defaultValue, flags); 47 | 48 | vmCvar->handle = nextCvarHandle_++; 49 | Cvar_Update(vmCvar); 50 | } 51 | 52 | void Cvar_Update(vmCvar_t* vmCvar) const 53 | { 54 | assert(vmCvar); 55 | 56 | assert(vmCvar->handle < cvars_.size()); 57 | auto const& cvar = cvars_[vmCvar->handle]; 58 | 59 | assert(cvar.string_.size() + 1 <= sizeof(vmCvar->string)); 60 | std::strncpy(vmCvar->string, cvar.string_.c_str(), sizeof(vmCvar->string) - 1); 61 | vmCvar->string[sizeof(vmCvar->string) - 1] = '\0'; 62 | 63 | vmCvar->value = cvar.value_; 64 | vmCvar->integer = cvar.integer_; 65 | } 66 | 67 | void Cvar_SetSafe(const char* var_name, const char* value) 68 | { 69 | assert(var_name); 70 | assert(value); 71 | 72 | auto* const cvar = find(var_name); 73 | assert(cvar); 74 | assert(!(cvar->flags_ & (CVAR_PROTECTED | CVAR_PRIVATE))); 75 | cvar->set(value); 76 | } 77 | 78 | void Cvar_VariableStringBufferSafe(char const* var_name, char* buffer, std::int32_t bufsize, std::int32_t flag) const 79 | { 80 | assert(var_name); 81 | assert(buffer); 82 | assert(bufsize > 0); 83 | 84 | auto const* const cvar = find(var_name); 85 | if (!cvar || cvar->flags_ & flag) 86 | { 87 | *buffer = '\0'; 88 | } 89 | else 90 | { 91 | std::strncpy(buffer, cvar->string_.c_str(), static_cast(bufsize - 1)); 92 | buffer[bufsize - 1] = '\0'; 93 | } 94 | } 95 | 96 | Cvar const* find(char const* var_name) const 97 | { 98 | assert(var_name); 99 | 100 | auto const nameToHandleIt = nameToHandles_.find(var_name); 101 | if (nameToHandleIt != nameToHandles_.cend()) 102 | { 103 | auto const handle = nameToHandleIt->second; 104 | assert(handle < cvars_.size()); 105 | return &cvars_[handle]; 106 | } 107 | return nullptr; 108 | } 109 | 110 | Cvar* find(char const* var_name) 111 | { 112 | return const_cast(static_cast(*this).find(var_name)); 113 | } 114 | 115 | public: 116 | cvarHandle_t nextCvarHandle_ = 0; 117 | std::vector cvars_; 118 | std::map nameToHandles_; 119 | }; 120 | 121 | void SyscallsCvarFake::setDefaultActions(SyscallsMock& mock) 122 | { 123 | ON_CALL(mock, Cvar_Register).WillByDefault(testing::Invoke(impl_.get(), &Impl::Cvar_Register)); 124 | ON_CALL(mock, Cvar_Update).WillByDefault(testing::Invoke(impl_.get(), &Impl::Cvar_Update)); 125 | ON_CALL(mock, Cvar_SetSafe).WillByDefault(testing::Invoke(impl_.get(), &Impl::Cvar_SetSafe)); 126 | ON_CALL(mock, Cvar_VariableStringBufferSafe) 127 | .WillByDefault(testing::Invoke(impl_.get(), &Impl::Cvar_VariableStringBufferSafe)); 128 | } 129 | 130 | SyscallsCvarFake::SyscallsCvarFake() : impl_(std::make_unique()) 131 | { 132 | } 133 | 134 | SyscallsCvarFake::~SyscallsCvarFake() = default; 135 | 136 | TEST(SyscallsCvarFake, Register) 137 | { 138 | SyscallsCvarFake::Impl fake; 139 | vmCvar_t vmCvar1 = {}; 140 | vmCvar_t vmCvar2 = {}; 141 | 142 | fake.Cvar_Register(&vmCvar1, "varName1", "3.14", CVAR_ARCHIVE_ND); 143 | fake.Cvar_Register(&vmCvar2, "varName2", "42", CVAR_ARCHIVE_ND); 144 | EXPECT_EQ(vmCvar1.value, 3.14f); 145 | EXPECT_EQ(vmCvar1.integer, 3); 146 | EXPECT_STREQ(vmCvar1.string, "3.14"); 147 | EXPECT_EQ(vmCvar2.value, 42.f); 148 | EXPECT_EQ(vmCvar2.integer, 42); 149 | EXPECT_STREQ(vmCvar2.string, "42"); 150 | } 151 | 152 | TEST(SyscallsCvarFake, Update) 153 | { 154 | SyscallsCvarFake::Impl fake; 155 | vmCvar_t vmCvar = {}; 156 | 157 | fake.Cvar_Register(&vmCvar, "varName", "3.14", CVAR_ARCHIVE_ND); 158 | fake.Cvar_SetSafe("varName", "42"); 159 | fake.Cvar_Update(&vmCvar); 160 | EXPECT_EQ(vmCvar.value, 42.f); 161 | EXPECT_EQ(vmCvar.integer, 42); 162 | EXPECT_STREQ(vmCvar.string, "42"); 163 | } 164 | 165 | TEST(SyscallsCvarFake, SetSafe) 166 | { 167 | SyscallsCvarFake::Impl fake; 168 | vmCvar_t vmCvar = {}; 169 | 170 | fake.Cvar_Register(&vmCvar, "varName", "3.14", CVAR_ARCHIVE_ND); 171 | fake.Cvar_SetSafe("varName", "42"); 172 | auto const* const cvar = fake.find("varName"); 173 | ASSERT_TRUE(cvar); 174 | EXPECT_EQ(cvar->value_, 42.f); 175 | EXPECT_EQ(cvar->integer_, 42); 176 | EXPECT_EQ(cvar->string_, "42"); 177 | EXPECT_EQ(vmCvar.value, 3.14f); 178 | EXPECT_EQ(vmCvar.integer, 3); 179 | EXPECT_STREQ(vmCvar.string, "3.14"); 180 | } 181 | 182 | TEST(SyscallsCvarFake, VariableStringBufferSafeNoCvar) 183 | { 184 | SyscallsCvarFake::Impl fake; 185 | char buffer[5] = "3.14"; 186 | 187 | fake.Cvar_VariableStringBufferSafe("varName", buffer, sizeof(buffer), CVAR_PRIVATE); 188 | EXPECT_EQ(std::strlen(buffer), 0u); 189 | } 190 | 191 | TEST(SyscallsCvarFake, VariableStringBufferSafeCvarPresent) 192 | { 193 | SyscallsCvarFake::Impl fake; 194 | vmCvar_t vmCvar = {}; 195 | char buffer[5] = {}; 196 | 197 | fake.Cvar_Register(&vmCvar, "varName", "3.14", CVAR_ARCHIVE_ND); 198 | fake.Cvar_VariableStringBufferSafe("varName", buffer, sizeof(buffer), CVAR_PRIVATE); 199 | EXPECT_STREQ(buffer, "3.14"); 200 | } 201 | 202 | TEST(SyscallsCvarFake, VariableStringBufferSafeWrongCvar) 203 | { 204 | SyscallsCvarFake::Impl fake; 205 | vmCvar_t vmCvar = {}; 206 | char buffer[5] = "3.14"; 207 | 208 | fake.Cvar_Register(&vmCvar, "varName", "3.14", CVAR_ARCHIVE_ND); 209 | fake.Cvar_VariableStringBufferSafe("WrongVarName", buffer, sizeof(buffer), CVAR_PRIVATE); 210 | EXPECT_EQ(std::strlen(buffer), 0u); 211 | } 212 | 213 | TEST(SyscallsCvarFake, VariableStringBufferSafePrivateCvar) 214 | { 215 | SyscallsCvarFake::Impl fake; 216 | vmCvar_t vmCvar = {}; 217 | char buffer[5] = "3.14"; 218 | 219 | fake.Cvar_Register(&vmCvar, "varName", "3.14", CVAR_PRIVATE); 220 | fake.Cvar_VariableStringBufferSafe("varName", buffer, sizeof(buffer), CVAR_PRIVATE); 221 | EXPECT_EQ(std::strlen(buffer), 0u); 222 | } 223 | -------------------------------------------------------------------------------- /test/syscalls_cvar_fake.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALLS_CVAR_FAKE_HPP 2 | #define SYSCALLS_CVAR_FAKE_HPP 3 | 4 | #include "syscalls_fake.hpp" 5 | 6 | #include 7 | 8 | class SyscallsCvarFake : public SyscallsFake 9 | { 10 | public: 11 | void setDefaultActions(SyscallsMock& mock) final; 12 | 13 | SyscallsCvarFake(); 14 | 15 | ~SyscallsCvarFake(); 16 | 17 | class Impl; 18 | 19 | private: 20 | std::unique_ptr impl_; 21 | }; 22 | 23 | #endif // SYSCALLS_CVAR_FAKE_HPP 24 | -------------------------------------------------------------------------------- /test/syscalls_fake.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALLS_FAKE_HPP 2 | #define SYSCALLS_FAKE_HPP 3 | 4 | class SyscallsMock; 5 | 6 | class SyscallsFake 7 | { 8 | public: 9 | virtual void setDefaultActions(SyscallsMock& mock) = 0; 10 | 11 | virtual ~SyscallsFake() = default; 12 | }; 13 | 14 | #endif // SYSCALLS_FAKE_HPP 15 | -------------------------------------------------------------------------------- /test/syscalls_mock.cpp: -------------------------------------------------------------------------------- 1 | #include "syscalls_mock.hpp" 2 | 3 | #include "syscalls_fake.hpp" 4 | 5 | extern "C" 6 | { 7 | #include 8 | } 9 | 10 | void SyscallsMock::delegateTo(SyscallsFake& fake) 11 | { 12 | fake.setDefaultActions(*this); 13 | } 14 | 15 | SyscallsMock::SyscallsMock() = default; 16 | 17 | SyscallsMock::~SyscallsMock() = default; 18 | 19 | TEST(SyscallsCvarMock, Register) 20 | { 21 | SyscallsMock mock; 22 | vmCvar_t vmCvar = {}; 23 | EXPECT_CALL(mock, Cvar_Register(&vmCvar, testing::StrEq("varName"), testing::StrEq("3.14"), CVAR_ARCHIVE_ND)) 24 | .Times(1); 25 | 26 | trap_Cvar_Register(&vmCvar, "varName", "3.14", CVAR_ARCHIVE_ND); 27 | } 28 | 29 | TEST(SyscallsCvarMock, Update) 30 | { 31 | SyscallsMock mock; 32 | vmCvar_t vmCvar = {}; 33 | EXPECT_CALL(mock, Cvar_Update(&vmCvar)).Times(1); 34 | 35 | trap_Cvar_Update(&vmCvar); 36 | } 37 | 38 | TEST(SyscallsCvarMock, SetSafe) 39 | { 40 | SyscallsMock mock; 41 | EXPECT_CALL(mock, Cvar_SetSafe(testing::StrEq("varName"), testing::StrEq("3.14"))).Times(1); 42 | 43 | trap_Cvar_Set("varName", "3.14"); 44 | } 45 | 46 | TEST(SyscallsCvarMock, VariableStringBufferSafe) 47 | { 48 | SyscallsMock mock; 49 | char buffer[5] = {}; 50 | EXPECT_CALL(mock, Cvar_VariableStringBufferSafe(testing::StrEq("varName"), buffer, sizeof(buffer), CVAR_PRIVATE)) 51 | .Times(1); 52 | 53 | trap_Cvar_VariableStringBuffer("varName", buffer, sizeof(buffer)); 54 | } 55 | 56 | TEST(SyscallsClientMock, GetCurrentSnapshotNumber) 57 | { 58 | SyscallsMock mock; 59 | std::int32_t snapshotNumber = 0; 60 | std::int32_t serverTime = 0; 61 | EXPECT_CALL(mock, CL_GetCurrentSnapshotNumber(&snapshotNumber, &serverTime)).Times(1); 62 | 63 | trap_GetCurrentSnapshotNumber(&snapshotNumber, &serverTime); 64 | } 65 | 66 | TEST(SyscallsClientMock, GetSnapshot) 67 | { 68 | SyscallsMock mock; 69 | snapshot_t snap = {}; 70 | EXPECT_CALL(mock, CL_GetSnapshot(0, &snap)).Times(1); 71 | 72 | trap_GetSnapshot(0, &snap); 73 | } 74 | -------------------------------------------------------------------------------- /test/syscalls_mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALLS_MOCK_HPP 2 | #define SYSCALLS_MOCK_HPP 3 | 4 | #include "syscalls.hpp" 5 | 6 | #include 7 | 8 | class SyscallsFake; 9 | 10 | class SyscallsMock : public Syscalls 11 | { 12 | public: 13 | MOCK_METHOD( 14 | void, 15 | Cvar_Register, 16 | (vmCvar_t * vmCvar, char const* varName, char const* defaultValue, std::int32_t flags), 17 | (final)); 18 | 19 | MOCK_METHOD(void, Cvar_Update, (vmCvar_t * vmCvar), (final)); 20 | 21 | MOCK_METHOD(void, Cvar_SetSafe, (char const* var_name, char const* value), (final)); 22 | 23 | MOCK_METHOD( 24 | void, 25 | Cvar_VariableStringBufferSafe, 26 | (char const* var_name, char* buffer, std::int32_t bufsize, std::int32_t flag), 27 | (final)); 28 | 29 | MOCK_METHOD(void, CL_GetCurrentSnapshotNumber, (std::int32_t * snapshotNumber, std::int32_t* serverTime), (final)); 30 | 31 | MOCK_METHOD(qboolean, CL_GetSnapshot, (std::int32_t snapshotNumber, snapshot_t* snapshot), (final)); 32 | 33 | void delegateTo(SyscallsFake& fake); 34 | 35 | SyscallsMock(); 36 | 37 | ~SyscallsMock(); 38 | }; 39 | 40 | #endif // SYSCALLS_MOCK_HPP 41 | -------------------------------------------------------------------------------- /version.h.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H 2 | #define VERSION_H 3 | 4 | // The configured options and settings 5 | #define VERSION "@CGAME_VERSION@" 6 | 7 | #endif // VERSION_H 8 | --------------------------------------------------------------------------------